VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / usb / gadget / dummy_hcd.c
index 637bbc1..4c1b157 100644 (file)
@@ -144,8 +144,7 @@ struct dummy {
        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
@@ -156,6 +155,10 @@ struct dummy {
        u32                             port_status;
        int                             started;
        struct completion               released;
+       unsigned                        resuming:1;
+       unsigned long                   re_timeout;
+
+       struct usb_device               *udev;
 };
 
 static struct dummy    *the_controller;
@@ -556,8 +559,37 @@ static int dummy_g_get_frame (struct usb_gadget *_gadget)
        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,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -653,6 +685,9 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
        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];
@@ -704,11 +739,9 @@ stop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
        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)
@@ -749,7 +782,6 @@ usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
 
        driver_unregister (&driver->driver);
 
-       del_timer_sync (&dum->timer);
        return 0;
 }
 EXPORT_SYMBOL (usb_gadget_unregister_driver);
@@ -790,8 +822,12 @@ static int dummy_urb_enqueue (
        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 */
@@ -959,10 +995,17 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
        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++) {
@@ -976,6 +1019,8 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
        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)
@@ -989,17 +1034,12 @@ static struct dummy_ep *find_endpoint (struct dummy *dum, u8 address)
 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:
@@ -1020,6 +1060,14 @@ static void dummy_timer (unsigned long _dum)
 
        /* 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;
@@ -1117,11 +1165,6 @@ restart:
                        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",
@@ -1130,8 +1173,19 @@ restart:
                                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,
@@ -1147,9 +1201,17 @@ restart:
                                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,
@@ -1185,6 +1247,10 @@ restart:
                break;
        }
        buf [0] = ep2->halted;
+                                               } else if (setup.bRequestType ==
+                                                               Dev_InRequest) {
+                                                       buf [0] = (u8)
+                                                               dum->devstatus;
                                                } else
                                                        buf [0] = 0;
                                        }
@@ -1273,7 +1339,11 @@ return_urb:
 
        /* 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);
 }
@@ -1338,8 +1408,20 @@ static int dummy_hub_control (
        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);
@@ -1350,33 +1432,28 @@ static int dummy_hub_control (
        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;
@@ -1395,8 +1472,42 @@ static int dummy_hub_control (
                                        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:
@@ -1469,10 +1580,12 @@ show_urbs (struct device *dev, char *buf)
        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);
@@ -1557,7 +1670,7 @@ clean0:
        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:
@@ -1571,14 +1684,16 @@ 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) {