mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
USB: gadget: gadget zero uses new suspend/resume hooks
Use the new device-level suspend/resume hooks for Gadget Zero; always enable them with the OTG test mode; and support remote wakeup on both configurations even in non-OTG mode. This ensures that both configurations can pass the USBCV remote wakeup tests when the OTG test mode is enabled. This changes behavior by adding autoresume support to the loopback config even in non-OTG mode; the test failure was that it didn't work in OTG mode. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
8942939a6c
commit
ab943a2e12
4 changed files with 74 additions and 58 deletions
|
@ -359,7 +359,7 @@ static struct usb_configuration loopback_driver = {
|
|||
* loopback_add - add a loopback testing configuration to a device
|
||||
* @cdev: the device to support the loopback configuration
|
||||
*/
|
||||
int __init loopback_add(struct usb_composite_dev *cdev)
|
||||
int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
|
||||
{
|
||||
int id;
|
||||
|
||||
|
@ -372,6 +372,10 @@ int __init loopback_add(struct usb_composite_dev *cdev)
|
|||
loopback_intf.iInterface = id;
|
||||
loopback_driver.iConfiguration = id;
|
||||
|
||||
/* support autoresume for remote wakeup testing */
|
||||
if (autoresume)
|
||||
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
|
||||
/* support OTG systems */
|
||||
if (gadget_is_otg(cdev->gadget)) {
|
||||
loopback_driver.descriptors = otg_desc;
|
||||
|
|
|
@ -59,7 +59,6 @@ struct f_sourcesink {
|
|||
|
||||
struct usb_ep *in_ep;
|
||||
struct usb_ep *out_ep;
|
||||
struct timer_list resume;
|
||||
};
|
||||
|
||||
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
||||
|
@ -67,10 +66,6 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
|||
return container_of(f, struct f_sourcesink, function);
|
||||
}
|
||||
|
||||
static unsigned autoresume;
|
||||
module_param(autoresume, uint, 0);
|
||||
MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
|
||||
|
||||
static unsigned pattern;
|
||||
module_param(pattern, uint, 0);
|
||||
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
|
||||
|
@ -155,21 +150,6 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void sourcesink_autoresume(unsigned long _c)
|
||||
{
|
||||
struct usb_composite_dev *cdev = (void *)_c;
|
||||
struct usb_gadget *g = cdev->gadget;
|
||||
|
||||
/* Normally the host would be woken up for something
|
||||
* more significant than just a timer firing; likely
|
||||
* because of some direct user request.
|
||||
*/
|
||||
if (g->speed != USB_SPEED_UNKNOWN) {
|
||||
int status = usb_gadget_wakeup(g);
|
||||
DBG(cdev, "%s --> %d\n", __func__, status);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init
|
||||
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
|
@ -198,9 +178,6 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
goto autoconf_fail;
|
||||
ss->out_ep->driver_data = cdev; /* claim */
|
||||
|
||||
setup_timer(&ss->resume, sourcesink_autoresume,
|
||||
(unsigned long) c->cdev);
|
||||
|
||||
/* support high speed hardware */
|
||||
if (gadget_is_dualspeed(c->cdev->gadget)) {
|
||||
hs_source_desc.bEndpointAddress =
|
||||
|
@ -359,7 +336,6 @@ static void disable_source_sink(struct f_sourcesink *ss)
|
|||
|
||||
cdev = ss->function.config->cdev;
|
||||
disable_endpoints(cdev, ss->in_ep, ss->out_ep);
|
||||
del_timer(&ss->resume);
|
||||
VDBG(cdev, "%s disabled\n", ss->function.name);
|
||||
}
|
||||
|
||||
|
@ -426,30 +402,6 @@ static void sourcesink_disable(struct usb_function *f)
|
|||
disable_source_sink(ss);
|
||||
}
|
||||
|
||||
static void sourcesink_suspend(struct usb_function *f)
|
||||
{
|
||||
struct f_sourcesink *ss = func_to_ss(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
|
||||
return;
|
||||
|
||||
if (autoresume) {
|
||||
mod_timer(&ss->resume, jiffies + (HZ * autoresume));
|
||||
DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
|
||||
} else
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
static void sourcesink_resume(struct usb_function *f)
|
||||
{
|
||||
struct f_sourcesink *ss = func_to_ss(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
del_timer(&ss->resume);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init sourcesink_bind_config(struct usb_configuration *c)
|
||||
|
@ -467,8 +419,6 @@ static int __init sourcesink_bind_config(struct usb_configuration *c)
|
|||
ss->function.unbind = sourcesink_unbind;
|
||||
ss->function.set_alt = sourcesink_set_alt;
|
||||
ss->function.disable = sourcesink_disable;
|
||||
ss->function.suspend = sourcesink_suspend;
|
||||
ss->function.resume = sourcesink_resume;
|
||||
|
||||
status = usb_add_function(c, &ss->function);
|
||||
if (status)
|
||||
|
@ -559,7 +509,7 @@ static struct usb_configuration sourcesink_driver = {
|
|||
* sourcesink_add - add a source/sink testing configuration to a device
|
||||
* @cdev: the device to support the configuration
|
||||
*/
|
||||
int __init sourcesink_add(struct usb_composite_dev *cdev)
|
||||
int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
|
||||
{
|
||||
int id;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ void disable_endpoints(struct usb_composite_dev *cdev,
|
|||
struct usb_ep *in, struct usb_ep *out);
|
||||
|
||||
/* configuration-specific linkup */
|
||||
int sourcesink_add(struct usb_composite_dev *cdev);
|
||||
int loopback_add(struct usb_composite_dev *cdev);
|
||||
int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume);
|
||||
int loopback_add(struct usb_composite_dev *cdev, bool autoresume);
|
||||
|
||||
#endif /* __G_ZERO_H */
|
||||
|
|
|
@ -102,11 +102,21 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
|
|||
#ifndef CONFIG_USB_ZERO_HNPTEST
|
||||
#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */
|
||||
#define DEFAULT_AUTORESUME 0
|
||||
#else
|
||||
#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */
|
||||
#define DRIVER_PRODUCT_NUM 0xbadd
|
||||
#define DEFAULT_AUTORESUME 5
|
||||
#endif
|
||||
|
||||
/* If the optional "autoresume" mode is enabled, it provides good
|
||||
* functional coverage for the "USBCV" test harness from USB-IF.
|
||||
* It's always set if OTG mode is enabled.
|
||||
*/
|
||||
unsigned autoresume = DEFAULT_AUTORESUME;
|
||||
module_param(autoresume, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
|
@ -212,6 +222,47 @@ void disable_endpoints(struct usb_composite_dev *cdev,
|
|||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct timer_list autoresume_timer;
|
||||
|
||||
static void zero_autoresume(unsigned long _c)
|
||||
{
|
||||
struct usb_composite_dev *cdev = (void *)_c;
|
||||
struct usb_gadget *g = cdev->gadget;
|
||||
|
||||
/* unconfigured devices can't issue wakeups */
|
||||
if (!cdev->config)
|
||||
return;
|
||||
|
||||
/* Normally the host would be woken up for something
|
||||
* more significant than just a timer firing; likely
|
||||
* because of some direct user request.
|
||||
*/
|
||||
if (g->speed != USB_SPEED_UNKNOWN) {
|
||||
int status = usb_gadget_wakeup(g);
|
||||
INFO(cdev, "%s --> %d\n", __func__, status);
|
||||
}
|
||||
}
|
||||
|
||||
static void zero_suspend(struct usb_composite_dev *cdev)
|
||||
{
|
||||
if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
|
||||
return;
|
||||
|
||||
if (autoresume) {
|
||||
mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
|
||||
DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
|
||||
} else
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
}
|
||||
|
||||
static void zero_resume(struct usb_composite_dev *cdev)
|
||||
{
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
del_timer(&autoresume_timer);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init zero_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int gcnum;
|
||||
|
@ -239,17 +290,19 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
|
|||
strings_dev[STRING_SERIAL_IDX].id = id;
|
||||
device_desc.iSerialNumber = id;
|
||||
|
||||
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
|
||||
|
||||
/* Register primary, then secondary configuration. Note that
|
||||
* SH3 only allows one config...
|
||||
*/
|
||||
if (loopdefault) {
|
||||
loopback_add(cdev);
|
||||
loopback_add(cdev, autoresume != 0);
|
||||
if (!gadget_is_sh(gadget))
|
||||
sourcesink_add(cdev);
|
||||
sourcesink_add(cdev, autoresume != 0);
|
||||
} else {
|
||||
sourcesink_add(cdev);
|
||||
sourcesink_add(cdev, autoresume != 0);
|
||||
if (!gadget_is_sh(gadget))
|
||||
loopback_add(cdev);
|
||||
loopback_add(cdev, autoresume != 0);
|
||||
}
|
||||
|
||||
gcnum = usb_gadget_controller_number(gadget);
|
||||
|
@ -278,11 +331,20 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int zero_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
del_timer_sync(&autoresume_timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_composite_driver zero_driver = {
|
||||
.name = "zero",
|
||||
.dev = &device_desc,
|
||||
.strings = dev_strings,
|
||||
.bind = zero_bind,
|
||||
.unbind = zero_unbind,
|
||||
.suspend = zero_suspend,
|
||||
.resume = zero_resume,
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
|
Loading…
Reference in a new issue