Greg:

This patch adds to the dummy_hcd driver a new routine for keeping track of 
all changes in the state of the emulated USB link.  The logic is now kept
in one spot instead of spread around, and it's easier to verify and 
update the code.  The behavior of the port features has been corrected in 
a few respects as well (for instance, if the POWER feature is clear then 
none of the other features can be set).

Also added is support for the (relatively new) _connect() and
_disconnect() calls of the Gadget API.

Alan Stern



Signed-off-by: Alan Stern <[EMAIL PROTECTED]>

Index: usb-2.6/drivers/usb/gadget/dummy_hcd.c
===================================================================
--- usb-2.6.orig/drivers/usb/gadget/dummy_hcd.c
+++ usb-2.6/drivers/usb/gadget/dummy_hcd.c
@@ -163,12 +163,16 @@ struct dummy {
        struct dummy_request            fifo_req;
        u8                              fifo_buf [FIFO_SIZE];
        u16                             devstatus;
+       unsigned                        pullup:1;
+       unsigned                        active:1;
+       unsigned                        old_active:1;
 
        /*
         * MASTER/HOST side support
         */
        struct timer_list               timer;
        u32                             port_status;
+       u32                             old_status;
        unsigned                        resuming:1;
        unsigned long                   re_timeout;
 
@@ -215,6 +219,98 @@ static struct dummy                        *the_controller;
 
 /*-------------------------------------------------------------------------*/
 
+/* SLAVE/GADGET SIDE UTILITY ROUTINES */
+
+/* called with spinlock held */
+static void nuke (struct dummy *dum, struct dummy_ep *ep)
+{
+       while (!list_empty (&ep->queue)) {
+               struct dummy_request    *req;
+
+               req = list_entry (ep->queue.next, struct dummy_request, queue);
+               list_del_init (&req->queue);
+               req->req.status = -ESHUTDOWN;
+
+               spin_unlock (&dum->lock);
+               req->req.complete (&ep->ep, &req->req);
+               spin_lock (&dum->lock);
+       }
+}
+
+/* caller must hold lock */
+static void
+stop_activity (struct dummy *dum)
+{
+       struct dummy_ep *ep;
+
+       /* prevent any more requests */
+       dum->address = 0;
+
+       /* 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)
+               nuke (dum, ep);
+
+       /* driver now does any non-usb quiescing necessary */
+}
+
+/* caller must hold lock */
+static void
+set_link_state (struct dummy *dum)
+{
+       dum->active = 0;
+       if ((dum->port_status & USB_PORT_STAT_POWER) == 0)
+               dum->port_status = 0;
+       else if (!dum->pullup) {
+               dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
+                                       USB_PORT_STAT_ENABLE |
+                                       USB_PORT_STAT_LOW_SPEED |
+                                       USB_PORT_STAT_HIGH_SPEED |
+                                       USB_PORT_STAT_SUSPEND);
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0)
+                       dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+       } else {
+               dum->port_status |= USB_PORT_STAT_CONNECTION;
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) == 0)
+                       dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+               if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0)
+                       dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+               else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0)
+                       dum->active = 1;
+       }
+
+       if ((dum->port_status & USB_PORT_STAT_ENABLE) == 0 || dum->active)
+               dum->resuming = 0;
+
+       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+                       (dum->port_status & USB_PORT_STAT_RESET) != 0) {
+               if ((dum->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
+                               (dum->old_status & USB_PORT_STAT_RESET) == 0 &&
+                               dum->driver) {
+                       stop_activity (dum);
+                       spin_unlock (&dum->lock);
+                       dum->driver->disconnect (&dum->gadget);
+                       spin_lock (&dum->lock);
+               }
+       } else if (dum->active != dum->old_active) {
+               if (dum->old_active && dum->driver->suspend) {
+                       spin_unlock (&dum->lock);
+                       dum->driver->suspend (&dum->gadget);
+                       spin_lock (&dum->lock);
+               } else if (!dum->old_active && dum->driver->resume) {
+                       spin_unlock (&dum->lock);
+                       dum->driver->resume (&dum->gadget);
+                       spin_lock (&dum->lock);
+               }
+       }
+
+       dum->old_status = dum->port_status;
+       dum->old_active = dum->active;
+}
+
+/*-------------------------------------------------------------------------*/
+
 /* SLAVE/GADGET SIDE DRIVER
  *
  * This only tracks gadget state.  All the work is done when the host
@@ -339,22 +435,6 @@ done:
        return retval;
 }
 
-/* called with spinlock held */
-static void nuke (struct dummy *dum, struct dummy_ep *ep)
-{
-       while (!list_empty (&ep->queue)) {
-               struct dummy_request    *req;
-
-               req = list_entry (ep->queue.next, struct dummy_request, queue);
-               list_del_init (&req->queue);
-               req->req.status = -ESHUTDOWN;
-
-               spin_unlock (&dum->lock);
-               req->req.complete (&ep->ep, &req->req);
-               spin_lock (&dum->lock);
-       }
-}
-
 static int dummy_disable (struct usb_ep *_ep)
 {
        struct dummy_ep         *ep;
@@ -603,7 +683,7 @@ static int dummy_wakeup (struct usb_gadg
 
        /* hub notices our request, issues downstream resume, etc */
        dum->resuming = 1;
-       dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+       dum->re_timeout = jiffies + msecs_to_jiffies(20);
        return 0;
 }
 
@@ -619,10 +699,24 @@ static int dummy_set_selfpowered (struct
        return 0;
 }
 
+static int dummy_pullup (struct usb_gadget *_gadget, int value)
+{
+       struct dummy    *dum;
+       unsigned long   flags;
+
+       dum = gadget_to_dummy (_gadget);
+       spin_lock_irqsave (&dum->lock, flags);
+       dum->pullup = (value != 0);
+       set_link_state (dum);
+       spin_unlock_irqrestore (&dum->lock, flags);
+       return 0;
+}
+
 static const struct usb_gadget_ops dummy_ops = {
        .get_frame      = dummy_g_get_frame,
        .wakeup         = dummy_wakeup,
        .set_selfpowered = dummy_set_selfpowered,
+       .pullup         = dummy_pullup,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -675,7 +769,6 @@ usb_gadget_register_driver (struct usb_g
         */
 
        dum->devstatus = 0;
-       dum->resuming = 0;
 
        INIT_LIST_HEAD (&dum->gadget.ep_list);
        for (i = 0; i < DUMMY_ENDPOINTS; i++) {
@@ -714,35 +807,14 @@ usb_gadget_register_driver (struct usb_g
        device_bind_driver (&dum->gadget.dev);
 
        /* khubd will enumerate this in a while */
-       dum->port_status |= USB_PORT_STAT_CONNECTION
-               | (USB_PORT_STAT_C_CONNECTION << 16);
+       spin_lock_irq (&dum->lock);
+       dum->pullup = 1;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
        return 0;
 }
 EXPORT_SYMBOL (usb_gadget_register_driver);
 
-/* caller must hold lock */
-static void
-stop_activity (struct dummy *dum, struct usb_gadget_driver *driver)
-{
-       struct dummy_ep *ep;
-
-       /* prevent any more requests */
-       dum->address = 0;
-
-       /* 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)
-               nuke (dum, ep);
-
-       /* driver now does any non-usb quiescing necessary */
-       if (driver) {
-               spin_unlock (&dum->lock);
-               driver->disconnect (&dum->gadget);
-               spin_lock (&dum->lock);
-       }
-}
-
 int
 usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
 {
@@ -758,10 +830,8 @@ usb_gadget_unregister_driver (struct usb
                        driver->driver.name);
 
        spin_lock_irqsave (&dum->lock, flags);
-       stop_activity (dum, driver);
-       dum->port_status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE |
-                       USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED);
-       dum->port_status |= (USB_PORT_STAT_C_CONNECTION << 16);
+       dum->pullup = 0;
+       set_link_state (dum);
        spin_unlock_irqrestore (&dum->lock, flags);
 
        driver->unbind (&dum->gadget);
@@ -770,6 +840,11 @@ usb_gadget_unregister_driver (struct usb
        device_release_driver (&dum->gadget.dev);
        driver_unregister (&driver->driver);
 
+       spin_lock_irqsave (&dum->lock, flags);
+       dum->pullup = 0;
+       set_link_state (dum);
+       spin_unlock_irqrestore (&dum->lock, flags);
+
        return 0;
 }
 EXPORT_SYMBOL (usb_gadget_unregister_driver);
@@ -1432,6 +1507,13 @@ static int dummy_hub_status (struct usb_
        dum = hcd_to_dummy (hcd);
 
        spin_lock_irqsave (&dum->lock, flags);
+
+       if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
+               dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
+               dum->port_status &= ~USB_PORT_STAT_SUSPEND;
+               set_link_state (dum);
+       }
+
        if (!(dum->port_status & PORT_C_MASK))
                retval = 0;
        else {
@@ -1480,16 +1562,16 @@ static int dummy_hub_control (
                                /* 20msec resume signaling */
                                dum->resuming = 1;
                                dum->re_timeout = jiffies +
-                                                       msecs_to_jiffies(20);
+                                               msecs_to_jiffies(20);
                        }
                        break;
                case USB_PORT_FEAT_POWER:
-                       dum->port_status = 0;
-                       dum->resuming = 0;
-                       stop_activity(dum, dum->driver);
-                       break;
+                       if (dum->port_status & USB_PORT_STAT_POWER)
+                               dev_dbg (dummy_dev(dum), "power-off\n");
+                       /* FALLS THROUGH */
                default:
                        dum->port_status &= ~(1 << wValue);
+                       set_link_state (dum);
                }
                break;
        case GetHubDescriptor:
@@ -1505,23 +1587,16 @@ static int dummy_hub_control (
                /* whoever resets or resumes must GetPortStatus to
                 * complete it!!
                 */
-               if (dum->resuming && time_after (jiffies, dum->re_timeout)) {
+               if (dum->resuming &&
+                               time_after_eq (jiffies, dum->re_timeout)) {
                        dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
                        dum->port_status &= ~USB_PORT_STAT_SUSPEND;
-                       dum->resuming = 0;
-                       dum->re_timeout = 0;
-                       if (dum->driver && dum->driver->resume) {
-                               spin_unlock (&dum->lock);
-                               dum->driver->resume (&dum->gadget);
-                               spin_lock (&dum->lock);
-                       }
                }
-               if ((dum->port_status & USB_PORT_STAT_RESET) != 0
-                               && time_after (jiffies, dum->re_timeout)) {
+               if ((dum->port_status & USB_PORT_STAT_RESET) != 0 &&
+                               time_after_eq (jiffies, dum->re_timeout)) {
                        dum->port_status |= (USB_PORT_STAT_C_RESET << 16);
                        dum->port_status &= ~USB_PORT_STAT_RESET;
-                       dum->re_timeout = 0;
-                       if (dum->driver) {
+                       if (dum->pullup) {
                                dum->port_status |= USB_PORT_STAT_ENABLE;
                                /* give it the best speed we agree on */
                                dum->gadget.speed = dum->driver->speed;
@@ -1542,6 +1617,7 @@ static int dummy_hub_control (
                                }
                        }
                }
+               set_link_state (dum);
                ((u16 *) buf)[0] = cpu_to_le16 (dum->port_status);
                ((u16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
                break;
@@ -1551,42 +1627,36 @@ static int dummy_hub_control (
        case SetPortFeature:
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
-                       if ((dum->port_status & USB_PORT_STAT_SUSPEND)
-                                       == 0) {
+                       if (dum->active) {
                                dum->port_status |= USB_PORT_STAT_SUSPEND;
-                               if (dum->driver && dum->driver->suspend) {
-                                       spin_unlock (&dum->lock);
-                                       dum->driver->suspend (&dum->gadget);
-                                       spin_lock (&dum->lock);
-                                       /* HNP would happen here; for now we
-                                        * assume b_bus_req is always true.
-                                        */
-                                       if (((1 << USB_DEVICE_B_HNP_ENABLE)
-                                                       & dum->devstatus) != 0)
-                                               dev_dbg (dummy_dev(dum),
+
+                               /* HNP would happen here; for now we
+                                * assume b_bus_req is always true.
+                                */
+                               set_link_state (dum);
+                               if (((1 << USB_DEVICE_B_HNP_ENABLE)
+                                               & dum->devstatus) != 0)
+                                       dev_dbg (dummy_dev(dum),
                                                        "no HNP yet!\n");
-                               }
                        }
                        break;
+               case USB_PORT_FEAT_POWER:
+                       dum->port_status |= USB_PORT_STAT_POWER;
+                       set_link_state (dum);
+                       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 (udc_dev(dum),
-                                                       "disconnect\n");
-                                       stop_activity (dum, dum->driver);
-                               }
-
-                               /* FIXME test that code path! */
-                       }
+                       /* if it's already enabled, disable */
+                       dum->port_status &= ~(USB_PORT_STAT_ENABLE
+                                       | USB_PORT_STAT_LOW_SPEED
+                                       | USB_PORT_STAT_HIGH_SPEED);
                        /* 50msec reset signaling */
                        dum->re_timeout = jiffies + msecs_to_jiffies(50);
-                       /* FALLTHROUGH */
+                       /* FALLS THROUGH */
                default:
-                       dum->port_status |= (1 << wValue);
+                       if ((dum->port_status & USB_PORT_STAT_POWER) != 0) {
+                               dum->port_status |= (1 << wValue);
+                               set_link_state (dum);
+                       }
                }
                break;
 



-------------------------------------------------------
This SF.Net email is sponsored by: NEC IT Guy Games.
Get your fingers limbered up and give it your best shot. 4 great events, 4
opportunities to win big! Highest score wins.NEC IT Guy Games. Play to
win an NEC 61 plasma display. Visit http://www.necitguy.com/?r=20
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to