Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / usb / gadget / inode.c
index 0ed297b..f1991d1 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/poll.h>
-
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/moduleparam.h>
 
@@ -112,6 +112,7 @@ enum ep0_state {
 struct dev_data {
        spinlock_t                      lock;
        atomic_t                        count;
+       int                             udc_usage;
        enum ep0_state                  state;          /* P: lock */
        struct usb_gadgetfs_event       event [N_EVENT];
        unsigned                        ev_next;
@@ -608,9 +609,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
                priv->actual = req->actual;
                kick_iocb(iocb);
        }
-       spin_unlock(&epdata->dev->lock);
 
        usb_ep_free_request(ep, req);
+       spin_unlock(&epdata->dev->lock);
        put_ep(epdata);
 }
 
@@ -1010,8 +1011,13 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                        struct usb_ep           *ep = dev->gadget->ep0;
                        struct usb_request      *req = dev->req;
 
-                       if ((retval = setup_req (ep, req, 0)) == 0)
-                               retval = usb_ep_queue (ep, req, GFP_ATOMIC);
+                       if ((retval = setup_req (ep, req, 0)) == 0) {
+                               ++dev->udc_usage;
+                               spin_unlock_irq (&dev->lock);
+                               retval = usb_ep_queue (ep, req, GFP_KERNEL);
+                               spin_lock_irq (&dev->lock);
+                               --dev->udc_usage;
+                       }
                        dev->state = STATE_DEV_CONNECTED;
 
                        /* assume that was SET_CONFIGURATION */
@@ -1052,11 +1058,14 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
                                retval = -EIO;
                        else {
                                len = min (len, (size_t)dev->req->actual);
-// FIXME don't call this with the spinlock held ...
+                               ++dev->udc_usage;
+                               spin_unlock_irq(&dev->lock);
                                if (copy_to_user (buf, dev->req->buf, len))
                                        retval = -EFAULT;
                                else
                                        retval = len;
+                               spin_lock_irq(&dev->lock);
+                               --dev->udc_usage;
                                clean_req (dev->gadget->ep0, dev->req);
                                /* NOTE userspace can't yet choose to stall */
                        }
@@ -1202,6 +1211,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                        retval = setup_req (dev->gadget->ep0, dev->req, len);
                        if (retval == 0) {
                                dev->state = STATE_DEV_CONNECTED;
+                               ++dev->udc_usage;
                                spin_unlock_irq (&dev->lock);
                                if (copy_from_user (dev->req->buf, buf, len))
                                        retval = -EFAULT;
@@ -1212,12 +1222,13 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                                                dev->gadget->ep0, dev->req,
                                                GFP_KERNEL);
                                }
+                               spin_lock_irq(&dev->lock);
+                               --dev->udc_usage;
                                if (retval < 0) {
-                                       spin_lock_irq (&dev->lock);
                                        clean_req (dev->gadget->ep0, dev->req);
-                                       spin_unlock_irq (&dev->lock);
                                } else
                                        retval = len;
+                               spin_unlock_irq(&dev->lock);
 
                                return retval;
                        }
@@ -1309,9 +1320,21 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
        struct usb_gadget       *gadget = dev->gadget;
        long ret = -ENOTTY;
 
-       if (gadget->ops->ioctl)
+       spin_lock_irq(&dev->lock);
+       if (dev->state == STATE_DEV_OPENED ||
+                       dev->state == STATE_DEV_UNBOUND) {
+               /* Not bound to a UDC */
+       } else if (gadget->ops->ioctl) {
+               ++dev->udc_usage;
+               spin_unlock_irq(&dev->lock);
+
                ret = gadget->ops->ioctl (gadget, code, value);
 
+               spin_lock_irq(&dev->lock);
+               --dev->udc_usage;
+       }
+       spin_unlock_irq(&dev->lock);
+
        return ret;
 }
 
@@ -1545,8 +1568,13 @@ delegate:
                                                        w_length);
                                if (value < 0)
                                        break;
+
+                               ++dev->udc_usage;
+                               spin_unlock (&dev->lock);
                                value = usb_ep_queue (gadget->ep0, dev->req,
-                                                       GFP_ATOMIC);
+                                                       GFP_KERNEL);
+                               spin_lock (&dev->lock);
+                               --dev->udc_usage;
                                if (value < 0) {
                                        clean_req (gadget->ep0, dev->req);
                                        break;
@@ -1569,11 +1597,18 @@ delegate:
        if (value >= 0 && dev->state != STATE_DEV_SETUP) {
                req->length = value;
                req->zero = value < w_length;
-               value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+
+               ++dev->udc_usage;
+               spin_unlock (&dev->lock);
+               value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
+               spin_lock(&dev->lock);
+               --dev->udc_usage;
+               spin_unlock(&dev->lock);
                if (value < 0) {
                        DBG (dev, "ep_queue --> %d\n", value);
                        req->status = 0;
                }
+               return value;
        }
 
        /* device stalls when value < 0 */
@@ -1598,21 +1633,24 @@ restart:
                /* break link to FS */
                ep = list_entry (entry, struct ep_data, epfiles);
                list_del_init (&ep->epfiles);
+               spin_unlock_irq (&dev->lock);
+
                dentry = ep->dentry;
                ep->dentry = NULL;
                parent = dentry->d_parent->d_inode;
 
                /* break link to controller */
+               mutex_lock(&ep->lock);
                if (ep->state == STATE_EP_ENABLED)
                        (void) usb_ep_disable (ep->ep);
                ep->state = STATE_EP_UNBOUND;
                usb_ep_free_request (ep->ep, ep->req);
                ep->ep = NULL;
+               mutex_unlock(&ep->lock);
+
                wake_up (&ep->wait);
                put_ep (ep);
 
-               spin_unlock_irq (&dev->lock);
-
                /* break link to dcache */
                mutex_lock (&parent->i_mutex);
                d_delete (dentry);
@@ -1686,6 +1724,11 @@ gadgetfs_unbind (struct usb_gadget *gadget)
 
        spin_lock_irq (&dev->lock);
        dev->state = STATE_DEV_UNBOUND;
+       while (dev->udc_usage > 0) {
+               spin_unlock_irq(&dev->lock);
+               usleep_range(1000, 2000);
+               spin_lock_irq(&dev->lock);
+       }
        spin_unlock_irq (&dev->lock);
 
        destroy_ep_files (dev);
@@ -1762,9 +1805,10 @@ static void
 gadgetfs_suspend (struct usb_gadget *gadget)
 {
        struct dev_data         *dev = get_gadget_data (gadget);
+       unsigned long           flags;
 
        INFO (dev, "suspended from state %d\n", dev->state);
-       spin_lock (&dev->lock);
+       spin_lock_irqsave(&dev->lock, flags);
        switch (dev->state) {
        case STATE_DEV_SETUP:           // VERY odd... host died??
        case STATE_DEV_CONNECTED:
@@ -1775,7 +1819,7 @@ gadgetfs_suspend (struct usb_gadget *gadget)
        default:
                break;
        }
-       spin_unlock (&dev->lock);
+       spin_unlock_irqrestore(&dev->lock, flags);
 }
 
 static struct usb_gadget_driver gadgetfs_driver = {