Commit 3861df46 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: dummy_hcd, root port wakeup/suspend

Here's what's in my tree to make dummy_hcd do suspend and
wakeup correctly ... that is, making its emulated root hub
and gadget work more like real ones.

It's easier to do this for fake hardware than the real stuff.
But real drivers tend to need very similar changes ... :)

- Dave

p.s. This does not depend on the suspend/resume patch.
      And it doesn't do "global" suspend (of root hub).
parent b9c5051b
...@@ -144,6 +144,7 @@ struct dummy { ...@@ -144,6 +144,7 @@ struct dummy {
struct usb_gadget_driver *driver; struct usb_gadget_driver *driver;
struct dummy_request fifo_req; struct dummy_request fifo_req;
u8 fifo_buf [FIFO_SIZE]; u8 fifo_buf [FIFO_SIZE];
u16 devstatus;
struct hcd_dev *hdev; struct hcd_dev *hdev;
...@@ -156,6 +157,8 @@ struct dummy { ...@@ -156,6 +157,8 @@ struct dummy {
u32 port_status; u32 port_status;
int started; int started;
struct completion released; struct completion released;
unsigned resuming:1;
unsigned long re_timeout;
}; };
static struct dummy *the_controller; static struct dummy *the_controller;
...@@ -556,8 +559,37 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget) ...@@ -556,8 +559,37 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget)
return tv.tv_usec / 1000; return tv.tv_usec / 1000;
} }
static int dummy_wakeup (struct usb_gadget *_gadget)
{
struct dummy *dum;
dum = container_of (_gadget, struct dummy, gadget);
if ((dum->devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) == 0
|| !(dum->port_status & (1 << USB_PORT_FEAT_SUSPEND)))
return -EINVAL;
/* hub notices our request, issues downstream resume, etc */
dum->resuming = 1;
dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
return 0;
}
static int dummy_set_selfpowered (struct usb_gadget *_gadget, int value)
{
struct dummy *dum;
dum = container_of (_gadget, struct dummy, gadget);
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
dum->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
return 0;
}
static const struct usb_gadget_ops dummy_ops = { static const struct usb_gadget_ops dummy_ops = {
.get_frame = dummy_g_get_frame, .get_frame = dummy_g_get_frame,
.wakeup = dummy_wakeup,
.set_selfpowered = dummy_set_selfpowered,
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -653,6 +685,9 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver) ...@@ -653,6 +685,9 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
dum->gadget.ops = &dummy_ops; dum->gadget.ops = &dummy_ops;
dum->gadget.is_dualspeed = 1; dum->gadget.is_dualspeed = 1;
dum->devstatus = 0;
dum->resuming = 0;
INIT_LIST_HEAD (&dum->gadget.ep_list); INIT_LIST_HEAD (&dum->gadget.ep_list);
for (i = 0; i < DUMMY_ENDPOINTS; i++) { for (i = 0; i < DUMMY_ENDPOINTS; i++) {
struct dummy_ep *ep = &dum->ep [i]; struct dummy_ep *ep = &dum->ep [i];
...@@ -1130,8 +1165,19 @@ static void dummy_timer (unsigned long _dum) ...@@ -1130,8 +1165,19 @@ static void dummy_timer (unsigned long _dum)
break; break;
case USB_REQ_SET_FEATURE: case USB_REQ_SET_FEATURE:
if (setup.bRequestType == Dev_Request) { if (setup.bRequestType == Dev_Request) {
// remote wakeup, and (hs) test mode value = 0;
switch (setup.wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
break;
default:
value = -EOPNOTSUPP; value = -EOPNOTSUPP;
}
if (value == 0) {
dum->devstatus |=
(1 << setup.wValue);
maybe_set_status (urb, 0);
}
} else if (setup.bRequestType == Ep_Request) { } else if (setup.bRequestType == Ep_Request) {
// endpoint halt // endpoint halt
ep2 = find_endpoint (dum, ep2 = find_endpoint (dum,
...@@ -1147,9 +1193,17 @@ static void dummy_timer (unsigned long _dum) ...@@ -1147,9 +1193,17 @@ static void dummy_timer (unsigned long _dum)
break; break;
case USB_REQ_CLEAR_FEATURE: case USB_REQ_CLEAR_FEATURE:
if (setup.bRequestType == Dev_Request) { if (setup.bRequestType == Dev_Request) {
// remote wakeup switch (setup.wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
dum->devstatus &= ~(1 <<
USB_DEVICE_REMOTE_WAKEUP);
value = 0; value = 0;
maybe_set_status (urb, 0); maybe_set_status (urb, 0);
break;
default:
value = -EOPNOTSUPP;
break;
}
} else if (setup.bRequestType == Ep_Request) { } else if (setup.bRequestType == Ep_Request) {
// endpoint halt // endpoint halt
ep2 = find_endpoint (dum, ep2 = find_endpoint (dum,
...@@ -1185,6 +1239,10 @@ static void dummy_timer (unsigned long _dum) ...@@ -1185,6 +1239,10 @@ static void dummy_timer (unsigned long _dum)
break; break;
} }
buf [0] = ep2->halted; buf [0] = ep2->halted;
} else if (setup.bRequestType ==
Dev_InRequest) {
buf [0] = (u8)
dum->devstatus;
} else } else
buf [0] = 0; buf [0] = 0;
} }
...@@ -1338,8 +1396,21 @@ static int dummy_hub_control ( ...@@ -1338,8 +1396,21 @@ static int dummy_hub_control (
case ClearHubFeature: case ClearHubFeature:
break; break;
case ClearPortFeature: case ClearPortFeature:
// FIXME won't some of these need special handling? switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
/* 20msec resume signaling */
dum->resuming = 1;
dum->re_timeout = jiffies + ((HZ * 20)/1000);
break;
case USB_PORT_FEAT_POWER:
dum->port_status = 0;
dum->address = 0;
dum->hdev = 0;
dum->resuming = 0;
break;
default:
dum->port_status &= ~(1 << wValue); dum->port_status &= ~(1 << wValue);
}
break; break;
case GetHubDescriptor: case GetHubDescriptor:
hub_descriptor ((struct usb_hub_descriptor *) buf); hub_descriptor ((struct usb_hub_descriptor *) buf);
...@@ -1350,33 +1421,28 @@ static int dummy_hub_control ( ...@@ -1350,33 +1421,28 @@ static int dummy_hub_control (
case GetPortStatus: case GetPortStatus:
if (wIndex != 1) if (wIndex != 1)
retval = -EPIPE; retval = -EPIPE;
((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
break;
case SetPortFeature:
if (wValue == USB_PORT_FEAT_RESET) {
/* if it's already running, disconnect first */
if (dum->port_status & USB_PORT_STAT_ENABLE) {
dum->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
if (dum->driver) {
dev_dbg (hardware, "disconnect\n");
stop_activity (dum, dum->driver);
}
/* FIXME test that code path! */ /* whoever resets or resumes must GetPortStatus to
} else * complete it!!
dum->port_status |= */
(1 << USB_PORT_FEAT_C_ENABLE); if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
dum->port_status |= USB_PORT_STAT_ENABLE | dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
(1 << USB_PORT_FEAT_C_RESET); dum->resuming = 0;
dum->re_timeout = 0;
if (dum->driver->resume) {
spin_unlock (&dum->lock);
dum->driver->resume (&dum->gadget);
spin_lock (&dum->lock);
}
}
if ((dum->port_status & (1 << USB_PORT_FEAT_RESET)) != 0
&& time_after (jiffies, dum->re_timeout)) {
dum->port_status |= (1 << USB_PORT_FEAT_C_RESET);
dum->port_status &= ~(1 << USB_PORT_FEAT_RESET);
dum->re_timeout = 0;
if (dum->driver) { if (dum->driver) {
dum->port_status |= USB_PORT_STAT_ENABLE;
/* give it the best speed we agree on */ /* give it the best speed we agree on */
dum->gadget.speed = dum->driver->speed; dum->gadget.speed = dum->driver->speed;
dum->gadget.ep0->maxpacket = 64; dum->gadget.ep0->maxpacket = 64;
...@@ -1395,8 +1461,42 @@ static int dummy_hub_control ( ...@@ -1395,8 +1461,42 @@ static int dummy_hub_control (
break; break;
} }
} }
} else }
((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
break;
case SetHubFeature:
retval = -EPIPE;
break;
case SetPortFeature:
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
dum->port_status |= (1 << USB_PORT_FEAT_SUSPEND);
if (dum->driver->suspend) {
spin_unlock (&dum->lock);
dum->driver->suspend (&dum->gadget);
spin_lock (&dum->lock);
}
break;
case USB_PORT_FEAT_RESET:
/* if it's already running, disconnect first */
if (dum->port_status & USB_PORT_STAT_ENABLE) {
dum->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
if (dum->driver) {
dev_dbg (hardware, "disconnect\n");
stop_activity (dum, dum->driver);
}
/* FIXME test that code path! */
}
/* 50msec reset signaling */
dum->re_timeout = jiffies + ((HZ * 50)/1000);
/* FALLTHROUGH */
default:
dum->port_status |= (1 << wValue); dum->port_status |= (1 << wValue);
}
break; break;
default: default:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment