]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/gadget/dummy_hcd.c
Merge branch 'master'
[linux-2.6-omap-h63xx.git] / drivers / usb / gadget / dummy_hcd.c
index 73d2f24050ab3cfde32f2dc6aead4f5861a8c6f6..02106bebd5c1ff147dbb070cac83b43d4003758e 100644 (file)
@@ -65,7 +65,7 @@
 
 
 #define DRIVER_DESC    "USB Host+Gadget Emulator"
-#define DRIVER_VERSION "17 Dec 2004"
+#define DRIVER_VERSION "02 May 2005"
 
 static const char      driver_name [] = "dummy_hcd";
 static const char      driver_desc [] = "USB Host+Gadget Emulator";
@@ -150,6 +150,13 @@ struct urbp {
        struct list_head        urbp_list;
 };
 
+
+enum dummy_rh_state {
+       DUMMY_RH_RESET,
+       DUMMY_RH_SUSPENDED,
+       DUMMY_RH_RUNNING
+};
+
 struct dummy {
        spinlock_t                      lock;
 
@@ -163,6 +170,7 @@ struct dummy {
        struct dummy_request            fifo_req;
        u8                              fifo_buf [FIFO_SIZE];
        u16                             devstatus;
+       unsigned                        udc_suspended:1;
        unsigned                        pullup:1;
        unsigned                        active:1;
        unsigned                        old_active:1;
@@ -170,6 +178,7 @@ struct dummy {
        /*
         * MASTER/HOST side support
         */
+       enum dummy_rh_state             rh_state;
        struct timer_list               timer;
        u32                             port_status;
        u32                             old_status;
@@ -262,7 +271,9 @@ 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) {
+
+       /* UDC suspend must cause a disconnect */
+       else if (!dum->pullup || dum->udc_suspended) {
                dum->port_status &= ~(USB_PORT_STAT_CONNECTION |
                                        USB_PORT_STAT_ENABLE |
                                        USB_PORT_STAT_LOW_SPEED |
@@ -276,7 +287,8 @@ set_link_state (struct dummy *dum)
                        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)
+               else if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+                               dum->rh_state != DUMMY_RH_SUSPENDED)
                        dum->active = 1;
        }
 
@@ -458,7 +470,7 @@ static int dummy_disable (struct usb_ep *_ep)
 }
 
 static struct usb_request *
-dummy_alloc_request (struct usb_ep *_ep, int mem_flags)
+dummy_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
 {
        struct dummy_ep         *ep;
        struct dummy_request    *req;
@@ -495,7 +507,7 @@ dummy_alloc_buffer (
        struct usb_ep *_ep,
        unsigned bytes,
        dma_addr_t *dma,
-       int mem_flags
+       gfp_t mem_flags
 ) {
        char                    *retval;
        struct dummy_ep         *ep;
@@ -528,7 +540,8 @@ fifo_complete (struct usb_ep *ep, struct usb_request *req)
 }
 
 static int
-dummy_queue (struct usb_ep *_ep, struct usb_request *_req, int mem_flags)
+dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
+               gfp_t mem_flags)
 {
        struct dummy_ep         *ep;
        struct dummy_request    *req;
@@ -675,11 +688,16 @@ static int dummy_wakeup (struct usb_gadget *_gadget)
        struct dummy    *dum;
 
        dum = gadget_to_dummy (_gadget);
-       if (!(dum->port_status & USB_PORT_STAT_SUSPEND)
-                       || !(dum->devstatus &
-                               ( (1 << USB_DEVICE_B_HNP_ENABLE)
+       if (!(dum->devstatus &  ( (1 << USB_DEVICE_B_HNP_ENABLE)
                                | (1 << USB_DEVICE_REMOTE_WAKEUP))))
                return -EINVAL;
+       if ((dum->port_status & USB_PORT_STAT_CONNECTION) == 0)
+               return -ENOLINK;
+       if ((dum->port_status & USB_PORT_STAT_SUSPEND) == 0 &&
+                        dum->rh_state != DUMMY_RH_SUSPENDED)
+               return -EIO;
+
+       /* FIXME: What if the root hub is suspended but the port isn't? */
 
        /* hub notices our request, issues downstream resume, etc */
        dum->resuming = 1;
@@ -734,7 +752,7 @@ show_function (struct device *dev, struct device_attribute *attr, char *buf)
                return 0;
        return scnprintf (buf, PAGE_SIZE, "%s\n", dum->driver->function);
 }
-DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
 
 /*-------------------------------------------------------------------------*/
 
@@ -857,6 +875,9 @@ EXPORT_SYMBOL (usb_gadget_unregister_driver);
 
 #undef is_enabled
 
+/* just declare this in any driver that really need it */
+extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
+
 int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode)
 {
        return -ENOSYS;
@@ -914,11 +935,44 @@ static int dummy_udc_remove (struct device *dev)
        return 0;
 }
 
+static int dummy_udc_suspend (struct device *dev, pm_message_t state)
+{
+       struct dummy    *dum = dev_get_drvdata(dev);
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       spin_lock_irq (&dum->lock);
+       dum->udc_suspended = 1;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+
+       dev->power.power_state = state;
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+       return 0;
+}
+
+static int dummy_udc_resume (struct device *dev)
+{
+       struct dummy    *dum = dev_get_drvdata(dev);
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       spin_lock_irq (&dum->lock);
+       dum->udc_suspended = 0;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+
+       dev->power.power_state = PMSG_ON;
+       usb_hcd_poll_rh_status (dummy_to_hcd (dum));
+       return 0;
+}
+
 static struct device_driver dummy_udc_driver = {
        .name           = (char *) gadget_name,
+       .owner          = THIS_MODULE,
        .bus            = &platform_bus_type,
        .probe          = dummy_udc_probe,
        .remove         = dummy_udc_remove,
+       .suspend        = dummy_udc_suspend,
+       .resume         = dummy_udc_resume,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -939,7 +993,7 @@ static int dummy_urb_enqueue (
        struct usb_hcd                  *hcd,
        struct usb_host_endpoint        *ep,
        struct urb                      *urb,
-       int                             mem_flags
+       gfp_t                           mem_flags
 ) {
        struct dummy    *dum;
        struct urbp     *urbp;
@@ -977,7 +1031,16 @@ static int dummy_urb_enqueue (
 
 static int dummy_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
-       /* giveback happens automatically in timer callback */
+       struct dummy    *dum;
+       unsigned long   flags;
+
+       /* giveback happens automatically in timer callback,
+        * so make sure the callback happens */
+       dum = hcd_to_dummy (hcd);
+       spin_lock_irqsave (&dum->lock, flags);
+       if (dum->rh_state != DUMMY_RH_RUNNING && !list_empty(&dum->urbp_list))
+               mod_timer (&dum->timer, jiffies);
+       spin_unlock_irqrestore (&dum->lock, flags);
        return 0;
 }
 
@@ -1122,7 +1185,6 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep)
 
                /* high bandwidth mode */
                tmp = le16_to_cpu(ep->desc->wMaxPacketSize);
-               tmp = le16_to_cpu (tmp);
                tmp = (tmp >> 11) & 0x03;
                tmp *= 8 /* applies to entire frame */;
                limit += limit * tmp;
@@ -1220,7 +1282,8 @@ restart:
                if (urb->status != -EINPROGRESS) {
                        /* likely it was just unlinked */
                        goto return_urb;
-               }
+               } else if (dum->rh_state != DUMMY_RH_RUNNING)
+                       continue;
                type = usb_pipetype (urb->pipe);
 
                /* used up this frame's non-periodic bandwidth?
@@ -1265,12 +1328,14 @@ restart:
                        struct usb_ctrlrequest          setup;
                        int                             value = 1;
                        struct dummy_ep                 *ep2;
+                       unsigned                        w_index;
+                       unsigned                        w_value;
 
                        setup = *(struct usb_ctrlrequest*) urb->setup_packet;
-                       le16_to_cpus (&setup.wIndex);
-                       le16_to_cpus (&setup.wValue);
-                       le16_to_cpus (&setup.wLength);
-                       if (setup.wLength != urb->transfer_buffer_length) {
+                       w_index = le16_to_cpu(setup.wIndex);
+                       w_value = le16_to_cpu(setup.wValue);
+                       if (le16_to_cpu(setup.wLength) !=
+                                       urb->transfer_buffer_length) {
                                maybe_set_status (urb, -EOVERFLOW);
                                goto return_urb;
                        }
@@ -1300,16 +1365,16 @@ restart:
                        case USB_REQ_SET_ADDRESS:
                                if (setup.bRequestType != Dev_Request)
                                        break;
-                               dum->address = setup.wValue;
+                               dum->address = w_value;
                                maybe_set_status (urb, 0);
                                dev_dbg (udc_dev(dum), "set_address = %d\n",
-                                               setup.wValue);
+                                               w_value);
                                value = 0;
                                break;
                        case USB_REQ_SET_FEATURE:
                                if (setup.bRequestType == Dev_Request) {
                                        value = 0;
-                                       switch (setup.wValue) {
+                                       switch (w_value) {
                                        case USB_DEVICE_REMOTE_WAKEUP:
                                                break;
                                        case USB_DEVICE_B_HNP_ENABLE:
@@ -1327,14 +1392,13 @@ restart:
                                        }
                                        if (value == 0) {
                                                dum->devstatus |=
-                                                       (1 << setup.wValue);
+                                                       (1 << w_value);
                                                maybe_set_status (urb, 0);
                                        }
 
                                } else if (setup.bRequestType == Ep_Request) {
                                        // endpoint halt
-                                       ep2 = find_endpoint (dum,
-                                                       setup.wIndex);
+                                       ep2 = find_endpoint (dum, w_index);
                                        if (!ep2) {
                                                value = -EOPNOTSUPP;
                                                break;
@@ -1346,7 +1410,7 @@ restart:
                                break;
                        case USB_REQ_CLEAR_FEATURE:
                                if (setup.bRequestType == Dev_Request) {
-                                       switch (setup.wValue) {
+                                       switch (w_value) {
                                        case USB_DEVICE_REMOTE_WAKEUP:
                                                dum->devstatus &= ~(1 <<
                                                        USB_DEVICE_REMOTE_WAKEUP);
@@ -1359,8 +1423,7 @@ restart:
                                        }
                                } else if (setup.bRequestType == Ep_Request) {
                                        // endpoint halt
-                                       ep2 = find_endpoint (dum,
-                                                       setup.wIndex);
+                                       ep2 = find_endpoint (dum, w_index);
                                        if (!ep2) {
                                                value = -EOPNOTSUPP;
                                                break;
@@ -1386,7 +1449,7 @@ restart:
                                        if (urb->transfer_buffer_length > 0) {
                                                if (setup.bRequestType ==
                                                                Ep_InRequest) {
-       ep2 = find_endpoint (dum, setup.wIndex);
+       ep2 = find_endpoint (dum, w_index);
        if (!ep2) {
                value = -EOPNOTSUPP;
                break;
@@ -1484,12 +1547,12 @@ return_urb:
                goto restart;
        }
 
-       /* want a 1 msec delay here */
-       if (!list_empty (&dum->urbp_list))
-               mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
-       else {
+       if (list_empty (&dum->urbp_list)) {
                usb_put_dev (dum->udev);
                dum->udev = NULL;
+       } else if (dum->rh_state == DUMMY_RH_RUNNING) {
+               /* want a 1 msec delay here */
+               mod_timer (&dum->timer, jiffies + msecs_to_jiffies(1));
        }
 
        spin_unlock_irqrestore (&dum->lock, flags);
@@ -1508,11 +1571,13 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
 {
        struct dummy            *dum;
        unsigned long           flags;
-       int                     retval;
+       int                     retval = 0;
 
        dum = hcd_to_dummy (hcd);
 
        spin_lock_irqsave (&dum->lock, flags);
+       if (hcd->state != HC_STATE_RUNNING)
+               goto done;
 
        if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) {
                dum->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
@@ -1520,14 +1585,15 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf)
                set_link_state (dum);
        }
 
-       if (!(dum->port_status & PORT_C_MASK))
-               retval = 0;
-       else {
+       if ((dum->port_status & PORT_C_MASK) != 0) {
                *buf = (1 << 1);
                dev_dbg (dummy_dev(dum), "port status 0x%08x has changes\n",
-                       dum->port_status);
+                               dum->port_status);
                retval = 1;
+               if (dum->rh_state == DUMMY_RH_SUSPENDED)
+                       usb_hcd_resume_root_hub (hcd);
        }
+done:
        spin_unlock_irqrestore (&dum->lock, flags);
        return retval;
 }
@@ -1538,7 +1604,8 @@ hub_descriptor (struct usb_hub_descriptor *desc)
        memset (desc, 0, sizeof *desc);
        desc->bDescriptorType = 0x29;
        desc->bDescLength = 9;
-       desc->wHubCharacteristics = __constant_cpu_to_le16 (0x0001);
+       desc->wHubCharacteristics = (__force __u16)
+                       (__constant_cpu_to_le16 (0x0001));
        desc->bNbrPorts = 1;
        desc->bitmap [0] = 0xff;
        desc->bitmap [1] = 0xff;
@@ -1556,6 +1623,9 @@ static int dummy_hub_control (
        int             retval = 0;
        unsigned long   flags;
 
+       if (hcd->state != HC_STATE_RUNNING)
+               return -ETIMEDOUT;
+
        dum = hcd_to_dummy (hcd);
        spin_lock_irqsave (&dum->lock, flags);
        switch (typeReq) {
@@ -1584,7 +1654,7 @@ static int dummy_hub_control (
                hub_descriptor ((struct usb_hub_descriptor *) buf);
                break;
        case GetHubStatus:
-               *(u32 *) buf = __constant_cpu_to_le32 (0);
+               *(__le32 *) buf = __constant_cpu_to_le32 (0);
                break;
        case GetPortStatus:
                if (wIndex != 1)
@@ -1624,8 +1694,8 @@ 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);
+               ((__le16 *) buf)[0] = cpu_to_le16 (dum->port_status);
+               ((__le16 *) buf)[1] = cpu_to_le16 (dum->port_status >> 16);
                break;
        case SetHubFeature:
                retval = -EPIPE;
@@ -1655,6 +1725,7 @@ static int dummy_hub_control (
                        dum->port_status &= ~(USB_PORT_STAT_ENABLE
                                        | USB_PORT_STAT_LOW_SPEED
                                        | USB_PORT_STAT_HIGH_SPEED);
+                       dum->devstatus = 0;
                        /* 50msec reset signaling */
                        dum->re_timeout = jiffies + msecs_to_jiffies(50);
                        /* FALLS THROUGH */
@@ -1681,6 +1752,29 @@ static int dummy_hub_control (
        return retval;
 }
 
+static int dummy_bus_suspend (struct usb_hcd *hcd)
+{
+       struct dummy *dum = hcd_to_dummy (hcd);
+
+       spin_lock_irq (&dum->lock);
+       dum->rh_state = DUMMY_RH_SUSPENDED;
+       set_link_state (dum);
+       spin_unlock_irq (&dum->lock);
+       return 0;
+}
+
+static int dummy_bus_resume (struct usb_hcd *hcd)
+{
+       struct dummy *dum = hcd_to_dummy (hcd);
+
+       spin_lock_irq (&dum->lock);
+       dum->rh_state = DUMMY_RH_RUNNING;
+       set_link_state (dum);
+       if (!list_empty(&dum->urbp_list))
+               mod_timer (&dum->timer, jiffies);
+       spin_unlock_irq (&dum->lock);
+       return 0;
+}
 
 /*-------------------------------------------------------------------------*/
 
@@ -1748,6 +1842,7 @@ static int dummy_start (struct usb_hcd *hcd)
        init_timer (&dum->timer);
        dum->timer.function = dummy_timer;
        dum->timer.data = (unsigned long) dum;
+       dum->rh_state = DUMMY_RH_RUNNING;
 
        INIT_LIST_HEAD (&dum->urbp_list);
 
@@ -1800,6 +1895,8 @@ static const struct hc_driver dummy_hcd = {
 
        .hub_status_data =      dummy_hub_status,
        .hub_control =          dummy_hub_control,
+       .bus_suspend =          dummy_bus_suspend,
+       .bus_resume =           dummy_bus_resume,
 };
 
 static int dummy_hcd_probe (struct device *dev)
@@ -1833,11 +1930,37 @@ static int dummy_hcd_remove (struct device *dev)
        return 0;
 }
 
+static int dummy_hcd_suspend (struct device *dev, pm_message_t state)
+{
+       struct usb_hcd          *hcd;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       hcd = dev_get_drvdata (dev);
+
+       hcd->state = HC_STATE_SUSPENDED;
+       return 0;
+}
+
+static int dummy_hcd_resume (struct device *dev)
+{
+       struct usb_hcd          *hcd;
+
+       dev_dbg (dev, "%s\n", __FUNCTION__);
+       hcd = dev_get_drvdata (dev);
+       hcd->state = HC_STATE_RUNNING;
+
+       usb_hcd_poll_rh_status (hcd);
+       return 0;
+}
+
 static struct device_driver dummy_hcd_driver = {
        .name           = (char *) driver_name,
+       .owner          = THIS_MODULE,
        .bus            = &platform_bus_type,
        .probe          = dummy_hcd_probe,
        .remove         = dummy_hcd_remove,
+       .suspend        = dummy_hcd_suspend,
+       .resume         = dummy_hcd_resume,
 };
 
 /*-------------------------------------------------------------------------*/