struct usb_gadget_driver *driver;
struct dummy_request fifo_req;
u8 fifo_buf [FIFO_SIZE];
-
- struct hcd_dev *hdev;
+ u16 devstatus;
/*
* MASTER/HOST side support
u32 port_status;
int started;
struct completion released;
+ unsigned resuming:1;
+ unsigned long re_timeout;
+
+ struct usb_device *udev;
};
static struct dummy *the_controller;
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 = {
.get_frame = dummy_g_get_frame,
+ .wakeup = dummy_wakeup,
+ .set_selfpowered = dummy_set_selfpowered,
};
/*-------------------------------------------------------------------------*/
dum->gadget.ops = &dummy_ops;
dum->gadget.is_dualspeed = 1;
+ dum->devstatus = 0;
+ dum->resuming = 0;
+
INIT_LIST_HEAD (&dum->gadget.ep_list);
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
struct dummy_ep *ep = &dum->ep [i];
struct dummy_ep *ep;
/* prevent any more requests */
- dum->hdev = 0;
dum->address = 0;
- /* this might not succeed ... */
- del_timer (&dum->timer);
+ /* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */
list_for_each_entry (ep, &dum->gadget.ep_list, ep.ep_list)
driver_unregister (&driver->driver);
- del_timer_sync (&dum->timer);
return 0;
}
EXPORT_SYMBOL (usb_gadget_unregister_driver);
dum = container_of (hcd, struct dummy, hcd);
spin_lock_irqsave (&dum->lock, flags);
- if (!dum->hdev)
- dum->hdev = urb->dev->hcpriv;
+ if (!dum->udev) {
+ dum->udev = urb->dev;
+ usb_get_dev (dum->udev);
+ } else if (unlikely (dum->udev != urb->dev))
+ dev_err (hardware, "usb_device address has changed!\n");
+
urb->hcpriv = dum;
if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */
return limit;
}
+#define is_active(dum) ((dum->port_status & \
+ (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE | \
+ USB_PORT_STAT_SUSPEND)) \
+ == (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE))
+
static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
{
int i;
+ if (!is_active (dum))
+ return NULL;
if ((address & ~USB_DIR_IN) == 0)
return &dum->ep [0];
for (i = 1; i < DUMMY_ENDPOINTS; i++) {
return NULL;
}
+#undef is_active
+
#define Dev_Request (USB_TYPE_STANDARD | USB_RECIP_DEVICE)
#define Dev_InRequest (Dev_Request | USB_DIR_IN)
#define Intf_Request (USB_TYPE_STANDARD | USB_RECIP_INTERFACE)
static void dummy_timer (unsigned long _dum)
{
struct dummy *dum = (struct dummy *) _dum;
- struct hcd_dev *hdev = dum->hdev;
+ struct hcd_dev *hdev;
struct list_head *entry, *tmp;
unsigned long flags;
int limit, total;
int i;
- if (!hdev) {
- dev_err (hardware, "timer fired with device gone?\n");
- return;
- }
-
/* simplistic model for one frame's bandwidth */
switch (dum->gadget.speed) {
case USB_SPEED_LOW:
/* look at each urb queued by the host side driver */
spin_lock_irqsave (&dum->lock, flags);
+
+ if (!dum->udev) {
+ dev_err (hardware, "timer fired with no URBs pending?\n");
+ spin_unlock_irqrestore (&dum->lock, flags);
+ return;
+ }
+ hdev = dum->udev->hcpriv;
+
for (i = 0; i < DUMMY_ENDPOINTS; i++) {
if (!ep_name [i])
break;
case USB_REQ_SET_ADDRESS:
if (setup.bRequestType != Dev_Request)
break;
- if (dum->address != 0) {
- maybe_set_status (urb, -ETIMEDOUT);
- urb->actual_length = 0;
- goto return_urb;
- }
dum->address = setup.wValue;
maybe_set_status (urb, 0);
dev_dbg (hardware, "set_address = %d\n",
break;
case USB_REQ_SET_FEATURE:
if (setup.bRequestType == Dev_Request) {
- // remote wakeup, and (hs) test mode
- value = -EOPNOTSUPP;
+ value = 0;
+ switch (setup.wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ break;
+ default:
+ value = -EOPNOTSUPP;
+ }
+ if (value == 0) {
+ dum->devstatus |=
+ (1 << setup.wValue);
+ maybe_set_status (urb, 0);
+ }
+
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
ep2 = find_endpoint (dum,
break;
case USB_REQ_CLEAR_FEATURE:
if (setup.bRequestType == Dev_Request) {
- // remote wakeup
- value = 0;
- maybe_set_status (urb, 0);
+ switch (setup.wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ dum->devstatus &= ~(1 <<
+ USB_DEVICE_REMOTE_WAKEUP);
+ value = 0;
+ maybe_set_status (urb, 0);
+ break;
+ default:
+ value = -EOPNOTSUPP;
+ break;
+ }
} else if (setup.bRequestType == Ep_Request) {
// endpoint halt
ep2 = find_endpoint (dum,
break;
}
buf [0] = ep2->halted;
+ } else if (setup.bRequestType ==
+ Dev_InRequest) {
+ buf [0] = (u8)
+ dum->devstatus;
} else
buf [0] = 0;
}
/* want a 1 msec delay here */
if (!list_empty (&hdev->urb_list))
- mod_timer (&dum->timer, jiffies + 1);
+ mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
+ else {
+ usb_put_dev (dum->udev);
+ dum->udev = NULL;
+ }
spin_unlock_irqrestore (&dum->lock, flags);
}
case ClearHubFeature:
break;
case ClearPortFeature:
- // FIXME won't some of these need special handling?
- dum->port_status &= ~(1 << wValue);
+ 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->resuming = 0;
+ stop_activity(dum, dum->driver);
+ break;
+ default:
+ dum->port_status &= ~(1 << wValue);
+ }
break;
case GetHubDescriptor:
hub_descriptor ((struct usb_hub_descriptor *) buf);
case GetPortStatus:
if (wIndex != 1)
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! */
- } else
- dum->port_status |=
- (1 << USB_PORT_FEAT_C_ENABLE);
- dum->port_status |= USB_PORT_STAT_ENABLE |
- (1 << USB_PORT_FEAT_C_RESET);
+ /* whoever resets or resumes must GetPortStatus to
+ * complete it!!
+ */
+ if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
+ dum->port_status |= (1 << USB_PORT_FEAT_C_SUSPEND);
+ dum->port_status &= ~(1 << USB_PORT_FEAT_SUSPEND);
+ 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) {
-
+ dum->port_status |= USB_PORT_STAT_ENABLE;
/* give it the best speed we agree on */
dum->gadget.speed = dum->driver->speed;
dum->gadget.ep0->maxpacket = 64;
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);
+ }
break;
default:
struct urb *urb;
size_t size = 0;
unsigned long flags;
+ struct hcd_dev *hdev;
spin_lock_irqsave (&dum->lock, flags);
- if (dum->hdev) {
- list_for_each_entry (urb, &dum->hdev->urb_list, urb_list) {
+ if (dum->udev) {
+ hdev = dum->udev->hcpriv;
+ list_for_each_entry (urb, &hdev->urb_list, urb_list) {
size_t temp;
temp = show_urb (buf, PAGE_SIZE - size, urb);
INIT_LIST_HEAD (&hcd->dev_list);
usb_register_bus (bus);
- bus->root_hub = root = usb_alloc_dev (0, bus, 0);
+ root = usb_alloc_dev (0, bus, 0);
if (!root) {
retval = -ENOMEM;
clean1:
root->speed = USB_SPEED_HIGH;
/* ...then configured, so khubd sees us. */
- if ((retval = hcd_register_root (&dum->hcd)) != 0) {
- bus->root_hub = 0;
+ if ((retval = hcd_register_root (root, &dum->hcd)) != 0) {
usb_put_dev (root);
clean2:
dum->hcd.state = USB_STATE_QUIESCING;
goto clean1;
}
+ /* only show a low-power port: just 8mA */
+ hub_set_power_budget (root, 8);
+
dum->started = 1;
if ((retval = dummy_register_udc (dum)) != 0) {