fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / gadget / inode.c
index 394bfbd..3fb1044 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * inode.c -- user mode filesystem api for usb gadget controllers
  *
- * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2004 David Brownell
  * Copyright (C) 2003 Agilent Technologies
  *
  * This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
  */
 
 
-#define        DEBUG 1                 /* data to help fault diagnosis */
+// #define     DEBUG                   /* data to help fault diagnosis */
 // #define     VERBOSE         /* extra debug messages (success too) */
 
 #include <linux/init.h>
@@ -32,6 +32,7 @@
 #include <linux/compiler.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
+#include <linux/poll.h>
 
 #include <linux/device.h>
 #include <linux/moduleparam.h>
@@ -44,7 +45,8 @@
  * The gadgetfs API maps each endpoint to a file descriptor so that you
  * can use standard synchronous read/write calls for I/O.  There's some
  * O_NONBLOCK and O_ASYNC/FASYNC style i/o support.  Example usermode
- * drivers show how this works in practice.
+ * drivers show how this works in practice.  You can also use AIO to
+ * eliminate I/O gaps between requests, to help when streaming data.
  *
  * Key parts that must be USB-specific are protocols defining how the
  * read/write operations relate to the hardware state machines.  There
@@ -70,7 +72,7 @@
  */
 
 #define        DRIVER_DESC     "USB Gadget filesystem"
-#define        DRIVER_VERSION  "20 Aug 2003"
+#define        DRIVER_VERSION  "24 Aug 2004"
 
 static const char driver_desc [] = DRIVER_DESC;
 static const char shortname [] = "gadgetfs";
@@ -134,6 +136,7 @@ struct dev_data {
                                        setup_out_ready : 1,
                                        setup_out_error : 1,
                                        setup_abort : 1;
+       unsigned                        setup_wLength;
 
        /* the rest is basically write-once */
        struct usb_config_descriptor    *config, *hs_config;
@@ -168,10 +171,9 @@ static struct dev_data *dev_new (void)
 {
        struct dev_data         *dev;
 
-       dev = kmalloc (sizeof *dev, GFP_KERNEL);
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
-               return 0;
-       memset (dev, 0, sizeof *dev);
+               return NULL;
        dev->state = STATE_DEV_DISABLED;
        atomic_set (&dev->count, 1);
        spin_lock_init (&dev->lock);
@@ -221,40 +223,18 @@ static void put_ep (struct ep_data *data)
        /* needs no more cleanup */
        BUG_ON (!list_empty (&data->epfiles));
        BUG_ON (waitqueue_active (&data->wait));
-       BUG_ON (down_trylock (&data->lock) != 0);
        kfree (data);
 }
 
 /*----------------------------------------------------------------------*/
 
 /* most "how to use the hardware" policy choices are in userspace:
- * mapping endpoint roles the driver needs to the capabilities that
- * the usb controller exposes.
+ * mapping endpoint roles (which the driver needs) to the capabilities
+ * which the usb controller has.  most of those capabilities are exposed
+ * implicitly, starting with the driver name and then endpoint names.
  */
 
-#ifdef CONFIG_USB_GADGET_DUMMY_HCD
-/* act (mostly) like a net2280 */
-#define CONFIG_USB_GADGET_NET2280
-#endif
-
-#ifdef CONFIG_USB_GADGET_NET2280
-#define CHIP                   "net2280"
-#define HIGHSPEED
-#endif
-
-#ifdef CONFIG_USB_GADGET_PXA2XX
-#define CHIP                   "pxa2xx_udc"
-/* earlier hardware doesn't have UDCCFR, races set_{config,interface} */
-#warning works best with pxa255 or newer
-#endif
-
-#ifdef CONFIG_USB_GADGET_GOKU
-#define CHIP                   "goku_udc"
-#endif
-
-#ifdef CONFIG_USB_GADGET_SA1100
-#define CHIP                   "sa1100"
-#endif
+static const char *CHIP;
 
 /*----------------------------------------------------------------------*/
 
@@ -295,7 +275,7 @@ static void put_ep (struct ep_data *data)
  *
  * After opening, configure non-control endpoints.  Then use normal
  * stream read() and write() requests; and maybe ioctl() to get more
- * precise FIFO status when recovering from cancelation.
+ * precise FIFO status when recovering from cancellation.
  */
 
 static void epio_complete (struct usb_ep *ep, struct usb_request *req)
@@ -362,7 +342,7 @@ fail:
 static ssize_t
 ep_io (struct ep_data *epdata, void *buf, unsigned len)
 {
-       DECLARE_COMPLETION (done);
+       DECLARE_COMPLETION_ONSTACK (done);
        int value;
 
        spin_lock_irq (&epdata->dev->lock);
@@ -432,13 +412,13 @@ ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
        /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
 
        value = -ENOMEM;
-       kbuf = kmalloc (len, SLAB_KERNEL);
+       kbuf = kmalloc (len, GFP_KERNEL);
        if (unlikely (!kbuf))
                goto free1;
 
        value = ep_io (data, kbuf, len);
-       VDEBUG (data->dev, "%s read %d OUT, status %d\n",
-               data->name, len, value);
+       VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
+               data->name, len, (int) value);
        if (value >= 0 && copy_to_user (buf, kbuf, value))
                value = -EFAULT;
 
@@ -476,7 +456,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
 
        value = -ENOMEM;
-       kbuf = kmalloc (len, SLAB_KERNEL);
+       kbuf = kmalloc (len, GFP_KERNEL);
        if (!kbuf)
                goto free1;
        if (copy_from_user (kbuf, buf, len)) {
@@ -485,8 +465,8 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        }
 
        value = ep_io (data, kbuf, len);
-       VDEBUG (data->dev, "%s write %d IN, status %d\n",
-               data->name, len, value);
+       VDEBUG (data->dev, "%s write %zu IN, status %d\n",
+               data->name, len, (int) value);
 free1:
        up (&data->lock);
        kfree (kbuf);
@@ -497,13 +477,19 @@ static int
 ep_release (struct inode *inode, struct file *fd)
 {
        struct ep_data          *data = fd->private_data;
+       int value;
+
+       if ((value = down_interruptible(&data->lock)) < 0)
+               return value;
 
        /* clean up if this can be reopened */
        if (data->state != STATE_EP_UNBOUND) {
                data->state = STATE_EP_DISABLED;
                data->desc.bDescriptorType = 0;
                data->hs_desc.bDescriptorType = 0;
+               usb_ep_disable(data->ep);
        }
+       up (&data->lock);
        put_ep (data);
        return 0;
 }
@@ -539,16 +525,236 @@ static int ep_ioctl (struct inode *inode, struct file *fd,
        return status;
 }
 
+/*----------------------------------------------------------------------*/
+
+/* ASYNCHRONOUS ENDPOINT I/O OPERATIONS (bulk/intr/iso) */
+
+struct kiocb_priv {
+       struct usb_request      *req;
+       struct ep_data          *epdata;
+       void                    *buf;
+       const struct iovec      *iv;
+       unsigned long           nr_segs;
+       unsigned                actual;
+};
+
+static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
+{
+       struct kiocb_priv       *priv = iocb->private;
+       struct ep_data          *epdata;
+       int                     value;
+
+       local_irq_disable();
+       epdata = priv->epdata;
+       // spin_lock(&epdata->dev->lock);
+       kiocbSetCancelled(iocb);
+       if (likely(epdata && epdata->ep && priv->req))
+               value = usb_ep_dequeue (epdata->ep, priv->req);
+       else
+               value = -EINVAL;
+       // spin_unlock(&epdata->dev->lock);
+       local_irq_enable();
+
+       aio_put_req(iocb);
+       return value;
+}
+
+static ssize_t ep_aio_read_retry(struct kiocb *iocb)
+{
+       struct kiocb_priv       *priv = iocb->private;
+       ssize_t                 len, total;
+       int                     i;
+
+       /* we "retry" to get the right mm context for this: */
+
+       /* copy stuff into user buffers */
+       total = priv->actual;
+       len = 0;
+       for (i=0; i < priv->nr_segs; i++) {
+               ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
+
+               if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) {
+                       if (len == 0)
+                               len = -EFAULT;
+                       break;
+               }
+
+               total -= this;
+               len += this;
+               if (total == 0)
+                       break;
+       }
+       kfree(priv->buf);
+       kfree(priv);
+       aio_put_req(iocb);
+       return len;
+}
+
+static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct kiocb            *iocb = req->context;
+       struct kiocb_priv       *priv = iocb->private;
+       struct ep_data          *epdata = priv->epdata;
+
+       /* lock against disconnect (and ideally, cancel) */
+       spin_lock(&epdata->dev->lock);
+       priv->req = NULL;
+       priv->epdata = NULL;
+       if (priv->iv == NULL
+                       || unlikely(req->actual == 0)
+                       || unlikely(kiocbIsCancelled(iocb))) {
+               kfree(req->buf);
+               kfree(priv);
+               iocb->private = NULL;
+               /* aio_complete() reports bytes-transferred _and_ faults */
+               if (unlikely(kiocbIsCancelled(iocb)))
+                       aio_put_req(iocb);
+               else
+                       aio_complete(iocb,
+                               req->actual ? req->actual : req->status,
+                               req->status);
+       } else {
+               /* retry() won't report both; so we hide some faults */
+               if (unlikely(0 != req->status))
+                       DBG(epdata->dev, "%s fault %d len %d\n",
+                               ep->name, req->status, req->actual);
+
+               priv->buf = req->buf;
+               priv->actual = req->actual;
+               kick_iocb(iocb);
+       }
+       spin_unlock(&epdata->dev->lock);
+
+       usb_ep_free_request(ep, req);
+       put_ep(epdata);
+}
+
+static ssize_t
+ep_aio_rwtail(
+       struct kiocb    *iocb,
+       char            *buf,
+       size_t          len,
+       struct ep_data  *epdata,
+       const struct iovec *iv,
+       unsigned long   nr_segs
+)
+{
+       struct kiocb_priv       *priv;
+       struct usb_request      *req;
+       ssize_t                 value;
+
+       priv = kmalloc(sizeof *priv, GFP_KERNEL);
+       if (!priv) {
+               value = -ENOMEM;
+fail:
+               kfree(buf);
+               return value;
+       }
+       iocb->private = priv;
+       priv->iv = iv;
+       priv->nr_segs = nr_segs;
+
+       value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
+       if (unlikely(value < 0)) {
+               kfree(priv);
+               goto fail;
+       }
+
+       iocb->ki_cancel = ep_aio_cancel;
+       get_ep(epdata);
+       priv->epdata = epdata;
+       priv->actual = 0;
+
+       /* each kiocb is coupled to one usb_request, but we can't
+        * allocate or submit those if the host disconnected.
+        */
+       spin_lock_irq(&epdata->dev->lock);
+       if (likely(epdata->ep)) {
+               req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
+               if (likely(req)) {
+                       priv->req = req;
+                       req->buf = buf;
+                       req->length = len;
+                       req->complete = ep_aio_complete;
+                       req->context = iocb;
+                       value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
+                       if (unlikely(0 != value))
+                               usb_ep_free_request(epdata->ep, req);
+               } else
+                       value = -EAGAIN;
+       } else
+               value = -ENODEV;
+       spin_unlock_irq(&epdata->dev->lock);
+
+       up(&epdata->lock);
+
+       if (unlikely(value)) {
+               kfree(priv);
+               put_ep(epdata);
+       } else
+               value = (iv ? -EIOCBRETRY : -EIOCBQUEUED);
+       return value;
+}
+
+static ssize_t
+ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
+               unsigned long nr_segs, loff_t o)
+{
+       struct ep_data          *epdata = iocb->ki_filp->private_data;
+       char                    *buf;
+
+       if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
+               return -EINVAL;
+
+       buf = kmalloc(iocb->ki_left, GFP_KERNEL);
+       if (unlikely(!buf))
+               return -ENOMEM;
+
+       iocb->ki_retry = ep_aio_read_retry;
+       return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs);
+}
+
+static ssize_t
+ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
+               unsigned long nr_segs, loff_t o)
+{
+       struct ep_data          *epdata = iocb->ki_filp->private_data;
+       char                    *buf;
+       size_t                  len = 0;
+       int                     i = 0;
+
+       if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
+               return -EINVAL;
+
+       buf = kmalloc(iocb->ki_left, GFP_KERNEL);
+       if (unlikely(!buf))
+               return -ENOMEM;
+
+       for (i=0; i < nr_segs; i++) {
+               if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
+                               iov[i].iov_len) != 0)) {
+                       kfree(buf);
+                       return -EFAULT;
+               }
+               len += iov[i].iov_len;
+       }
+       return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
+}
+
+/*----------------------------------------------------------------------*/
+
 /* used after endpoint configuration */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
        .owner =        THIS_MODULE,
+       .llseek =       no_llseek,
+
        .read =         ep_read,
        .write =        ep_write,
        .ioctl =        ep_ioctl,
        .release =      ep_release,
 
-       // .aio_read =  ep_aio_read,
-       // .aio_write = ep_aio_write,
+       .aio_read =     ep_aio_read,
+       .aio_write =    ep_aio_write,
 };
 
 /* ENDPOINT INITIALIZATION
@@ -565,12 +771,12 @@ static struct file_operations ep_io_operations = {
  * speed descriptor, then optional high speed descriptor.
  */
 static ssize_t
-ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
        struct ep_data          *data = fd->private_data;
        struct usb_ep           *ep;
        u32                     tag;
-       int                     value;
+       int                     value, length = len;
 
        if ((value = down_interruptible (&data->lock)) < 0)
                return value;
@@ -621,7 +827,6 @@ ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
                        goto fail0;
                }
        }
-       value = len;
 
        spin_lock_irq (&data->dev->lock);
        if (data->dev->state == STATE_DEV_UNBOUND) {
@@ -638,7 +843,7 @@ ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
                if (value == 0)
                        data->state = STATE_EP_ENABLED;
                break;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        case USB_SPEED_HIGH:
                /* fails if caller didn't provide that descriptor... */
                value = usb_ep_enable (ep, &data->hs_desc);
@@ -651,8 +856,10 @@ ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
                                data->name);
                data->state = STATE_EP_DEFER_ENABLE;
        }
-       if (value == 0)
+       if (value == 0) {
                fd->f_op = &ep_io_operations;
+               value = length;
+       }
 gone:
        spin_unlock_irq (&data->dev->lock);
        if (value < 0) {
@@ -673,7 +880,7 @@ fail1:
 static int
 ep_open (struct inode *inode, struct file *fd)
 {
-       struct ep_data          *data = inode->u.generic_ip;
+       struct ep_data          *data = inode->i_private;
        int                     value = -EBUSY;
 
        if (down_interruptible (&data->lock) != 0)
@@ -696,8 +903,10 @@ ep_open (struct inode *inode, struct file *fd)
 }
 
 /* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
        .owner =        THIS_MODULE,
+       .llseek =       no_llseek,
+
        .open =         ep_open,
        .write =        ep_config,
        .release =      ep_release,
@@ -768,11 +977,12 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
        }
        req->complete = ep0_complete;
        req->length = len;
+       req->zero = 0;
        return 0;
 }
 
 static ssize_t
-ep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr)
+ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 {
        struct dev_data                 *dev = fd->private_data;
        ssize_t                         retval;
@@ -804,6 +1014,18 @@ ep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr)
                                retval = usb_ep_queue (ep, req, GFP_ATOMIC);
                        dev->state = STATE_CONNECTED;
 
+                       /* assume that was SET_CONFIGURATION */
+                       if (dev->current_config) {
+                               unsigned power;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+                               if (dev->gadget->speed == USB_SPEED_HIGH)
+                                       power = dev->hs_config->bMaxPower;
+                               else
+#endif
+                                       power = dev->config->bMaxPower;
+                               usb_gadget_vbus_draw(dev->gadget, 2 * power);
+                       }
+
                } else {                        /* collect OUT data */
                        if ((fd->f_flags & O_NONBLOCK) != 0
                                        && !dev->setup_out_ready) {
@@ -823,7 +1045,7 @@ ep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr)
                        else {
                                len = min (len, (size_t)dev->req->actual);
 // FIXME don't call this with the spinlock held ...
-                               if (copy_to_user (buf, &dev->req->buf, len))
+                               if (copy_to_user (buf, dev->req->buf, len))
                                        retval = -EFAULT;
                                clean_req (dev->gadget->ep0, dev->req);
                                /* NOTE userspace can't yet choose to stall */
@@ -852,7 +1074,7 @@ scan:
                /* ep0 can't deliver events when STATE_SETUP */
                for (i = 0; i < n; i++) {
                        if (dev->event [i].type == GADGETFS_SETUP) {
-                               len = n = i + 1;
+                               len = i + 1;
                                len *= sizeof (struct usb_gadgetfs_event);
                                n = 0;
                                break;
@@ -953,7 +1175,7 @@ next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type)
 }
 
 static ssize_t
-ep0_write (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
        struct dev_data         *dev = fd->private_data;
        ssize_t                 retval = -ESRCH;
@@ -975,10 +1197,13 @@ ep0_write (struct file *fd, const char *buf, size_t len, loff_t *ptr)
                                spin_unlock_irq (&dev->lock);
                                if (copy_from_user (dev->req->buf, buf, len))
                                        retval = -EFAULT;
-                               else
+                               else {
+                                       if (len < dev->setup_wLength)
+                                               dev->req->zero = 1;
                                        retval = usb_ep_queue (
                                                dev->gadget->ep0, dev->req,
                                                GFP_KERNEL);
+                               }
                                if (retval < 0) {
                                        spin_lock_irq (&dev->lock);
                                        clean_req (dev->gadget->ep0, dev->req);
@@ -1032,7 +1257,7 @@ dev_release (struct inode *inode, struct file *fd)
 
        fasync_helper (-1, fd, 0, &dev->fasync);
        kfree (dev->buf);
-       dev->buf = 0;
+       dev->buf = NULL;
        put_dev (dev);
 
        /* other endpoints were all decoupled from this device */
@@ -1040,6 +1265,35 @@ dev_release (struct inode *inode, struct file *fd)
        return 0;
 }
 
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+       struct dev_data         *dev = fd->private_data;
+       int                     mask = 0;
+
+       poll_wait(fd, &dev->wait, wait);
+
+       spin_lock_irq (&dev->lock);
+
+       /* report fd mode change before acting on it */
+       if (dev->setup_abort) {
+               dev->setup_abort = 0;
+               mask = POLLHUP;
+               goto out;
+       }
+
+       if (dev->state == STATE_SETUP) {
+               if (dev->setup_in || dev->setup_can_stall)
+                       mask = POLLOUT;
+       } else {
+               if (dev->ev_next != 0)
+                       mask = POLLIN;
+       }
+out:
+       spin_unlock_irq(&dev->lock);
+       return mask;
+}
+
 static int dev_ioctl (struct inode *inode, struct file *fd,
                unsigned code, unsigned long value)
 {
@@ -1052,12 +1306,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
 }
 
 /* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
        .owner =        THIS_MODULE,
+       .llseek =       no_llseek,
+
        .read =         ep0_read,
        .write =        ep0_write,
        .fasync =       ep0_fasync,
-       // .poll =      ep0_poll,
+       .poll =         ep0_poll,
        .ioctl =        dev_ioctl,
        .release =      dev_release,
 };
@@ -1070,7 +1326,7 @@ static struct file_operations ep0_io_operations = {
  * Unrecognized ep0 requests may be handled in user space.
  */
 
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 static void make_qualifier (struct dev_data *dev)
 {
        struct usb_qualifier_descriptor         qual;
@@ -1099,7 +1355,7 @@ static int
 config_buf (struct dev_data *dev, u8 type, unsigned index)
 {
        int             len;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        int             hs;
 #endif
 
@@ -1107,7 +1363,7 @@ config_buf (struct dev_data *dev, u8 type, unsigned index)
        if (index > 0)
                return -EINVAL;
 
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        hs = (dev->gadget->speed == USB_SPEED_HIGH);
        if (type == USB_DT_OTHER_SPEED_CONFIG)
                hs = !hs;
@@ -1131,6 +1387,8 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        struct usb_request              *req = dev->req;
        int                             value = -EOPNOTSUPP;
        struct usb_gadgetfs_event       *event;
+       u16                             w_value = le16_to_cpu(ctrl->wValue);
+       u16                             w_length = le16_to_cpu(ctrl->wLength);
 
        spin_lock (&dev->lock);
        dev->setup_abort = 0;
@@ -1141,12 +1399,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                dev->state = STATE_CONNECTED;
                dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket;
 
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
                if (gadget->speed == USB_SPEED_HIGH && dev->hs_config == 0) {
                        ERROR (dev, "no high speed config??\n");
                        return -EINVAL;
                }
-#endif /* HIGHSPEED */
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
 
                INFO (dev, "connected\n");
                event = next_event (dev, GADGETFS_CONNECT);
@@ -1158,11 +1416,11 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                        /* ... down_trylock (&data->lock) ... */
                        if (data->state != STATE_EP_DEFER_ENABLE)
                                continue;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
                        if (gadget->speed == USB_SPEED_HIGH)
                                value = usb_ep_enable (ep, &data->hs_desc);
                        else
-#endif /* HIGHSPEED */
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
                                value = usb_ep_enable (ep, &data->desc);
                        if (value) {
                                ERROR (dev, "deferred %s enable --> %d\n",
@@ -1184,24 +1442,24 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 
        req->buf = dev->rbuf;
        req->dma = DMA_ADDR_INVALID;
-       req->context = 0;
+       req->context = NULL;
        value = -EOPNOTSUPP;
        switch (ctrl->bRequest) {
 
        case USB_REQ_GET_DESCRIPTOR:
                if (ctrl->bRequestType != USB_DIR_IN)
                        goto unrecognized;
-               switch (ctrl->wValue >> 8) {
+               switch (w_value >> 8) {
 
                case USB_DT_DEVICE:
-                       value = min (ctrl->wLength, (u16) sizeof *dev->dev);
+                       value = min (w_length, (u16) sizeof *dev->dev);
                        req->buf = dev->dev;
                        break;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
                case USB_DT_DEVICE_QUALIFIER:
                        if (!dev->hs_config)
                                break;
-                       value = min (ctrl->wLength, (u16)
+                       value = min (w_length, (u16)
                                sizeof (struct usb_qualifier_descriptor));
                        make_qualifier (dev);
                        break;
@@ -1210,10 +1468,10 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 #endif
                case USB_DT_CONFIG:
                        value = config_buf (dev,
-                                       ctrl->wValue >> 8,
-                                       ctrl->wValue & 0xff);
+                                       w_value >> 8,
+                                       w_value & 0xff);
                        if (value >= 0)
-                               value = min (ctrl->wLength, (u16) value);
+                               value = min (w_length, (u16) value);
                        break;
                case USB_DT_STRING:
                        goto unrecognized;
@@ -1227,22 +1485,28 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        case USB_REQ_SET_CONFIGURATION:
                if (ctrl->bRequestType != 0)
                        break;
-               if (0 == (u8) ctrl->wValue) {
+               if (0 == (u8) w_value) {
                        value = 0;
                        dev->current_config = 0;
+                       usb_gadget_vbus_draw(gadget, 8 /* mA */ );
                        // user mode expected to disable endpoints
                } else {
-                       u8      config;
-#ifdef HIGHSPEED
-                       if (gadget->speed == USB_SPEED_HIGH)
+                       u8      config, power;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+                       if (gadget->speed == USB_SPEED_HIGH) {
                                config = dev->hs_config->bConfigurationValue;
-                       else
+                               power = dev->hs_config->bMaxPower;
+                       } else
 #endif
+                       {
                                config = dev->config->bConfigurationValue;
+                               power = dev->config->bMaxPower;
+                       }
 
-                       if (config == (u8) ctrl->wValue) {
+                       if (config == (u8) w_value) {
                                value = 0;
                                dev->current_config = config;
+                               usb_gadget_vbus_draw(gadget, 2 * power);
                        }
                }
 
@@ -1270,7 +1534,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
                if (ctrl->bRequestType != 0x80)
                        break;
                *(u8 *)req->buf = dev->current_config;
-               value = min (ctrl->wLength, (u16) 1);
+               value = min (w_length, (u16) 1);
                break;
 #endif
 
@@ -1279,7 +1543,7 @@ unrecognized:
                VDEBUG (dev, "%s req%02x.%02x v%04x i%04x l%d\n",
                        dev->usermode_setup ? "delegate" : "fail",
                        ctrl->bRequestType, ctrl->bRequest,
-                       ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+                       w_value, le16_to_cpu(ctrl->wIndex), w_length);
 
                /* if there's an ep0 reader, don't stall */
                if (dev->usermode_setup) {
@@ -1287,14 +1551,15 @@ unrecognized:
 delegate:
                        dev->setup_in = (ctrl->bRequestType & USB_DIR_IN)
                                                ? 1 : 0;
+                       dev->setup_wLength = w_length;
                        dev->setup_out_ready = 0;
                        dev->setup_out_error = 0;
                        value = 0;
 
                        /* read DATA stage for OUT right away */
-                       if (unlikely (!dev->setup_in && ctrl->wLength)) {
+                       if (unlikely (!dev->setup_in && w_length)) {
                                value = setup_req (gadget->ep0, dev->req,
-                                                       ctrl->wLength);
+                                                       w_length);
                                if (value < 0)
                                        break;
                                value = usb_ep_queue (gadget->ep0, dev->req,
@@ -1320,6 +1585,7 @@ delegate:
        /* proceed with data transfer and status phases? */
        if (value >= 0 && dev->state != STATE_SETUP) {
                req->length = value;
+               req->zero = value < w_length;
                value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
                if (value < 0) {
                        DBG (dev, "ep_queue --> %d\n", value);
@@ -1350,7 +1616,7 @@ restart:
                ep = list_entry (entry, struct ep_data, epfiles);
                list_del_init (&ep->epfiles);
                dentry = ep->dentry;
-               ep->dentry = 0;
+               ep->dentry = NULL;
                parent = dentry->d_parent->d_inode;
 
                /* break link to controller */
@@ -1358,17 +1624,17 @@ restart:
                        (void) usb_ep_disable (ep->ep);
                ep->state = STATE_EP_UNBOUND;
                usb_ep_free_request (ep->ep, ep->req);
-               ep->ep = 0;
+               ep->ep = NULL;
                wake_up (&ep->wait);
                put_ep (ep);
 
                spin_unlock_irq (&dev->lock);
 
                /* break link to dcache */
-               down (&parent->i_sem);
+               mutex_lock (&parent->i_mutex);
                d_delete (dentry);
                dput (dentry);
-               up (&parent->i_sem);
+               mutex_unlock (&parent->i_mutex);
 
                /* fds may still be open */
                goto restart;
@@ -1379,20 +1645,19 @@ restart:
 
 static struct inode *
 gadgetfs_create_file (struct super_block *sb, char const *name,
-               void *data, struct file_operations *fops,
+               void *data, const struct file_operations *fops,
                struct dentry **dentry_p);
 
 static int activate_ep_files (struct dev_data *dev)
 {
        struct usb_ep   *ep;
+       struct ep_data  *data;
 
        gadget_for_each_ep (ep, dev->gadget) {
-               struct ep_data  *data;
 
-               data = kmalloc (sizeof *data, GFP_KERNEL);
+               data = kzalloc(sizeof(*data), GFP_KERNEL);
                if (!data)
-                       goto enomem;
-               memset (data, 0, sizeof data);
+                       goto enomem0;
                data->state = STATE_EP_DISABLED;
                init_MUTEX (&data->lock);
                init_waitqueue_head (&data->wait);
@@ -1407,20 +1672,23 @@ static int activate_ep_files (struct dev_data *dev)
 
                data->req = usb_ep_alloc_request (ep, GFP_KERNEL);
                if (!data->req)
-                       goto enomem;
+                       goto enomem1;
 
                data->inode = gadgetfs_create_file (dev->sb, data->name,
                                data, &ep_config_operations,
                                &data->dentry);
-               if (!data->inode) {
-                       kfree (data);
-                       goto enomem;
-               }
+               if (!data->inode)
+                       goto enomem2;
                list_add_tail (&data->epfiles, &dev->epfiles);
        }
        return 0;
 
-enomem:
+enomem2:
+       usb_ep_free_request (ep, data->req);
+enomem1:
+       put_dev (dev);
+       kfree (data);
+enomem0:
        DBG (dev, "%s enomem\n", __FUNCTION__);
        destroy_ep_files (dev);
        return -ENOMEM;
@@ -1438,8 +1706,8 @@ gadgetfs_unbind (struct usb_gadget *gadget)
        spin_unlock_irq (&dev->lock);
 
        destroy_ep_files (dev);
-       gadget->ep0->driver_data = 0;
-       set_gadget_data (gadget, 0);
+       gadget->ep0->driver_data = NULL;
+       set_gadget_data (gadget, NULL);
 
        /* we've already been disconnected ... no i/o is active */
        if (dev->req)
@@ -1458,8 +1726,8 @@ gadgetfs_bind (struct usb_gadget *gadget)
        if (!dev)
                return -ESRCH;
        if (0 != strcmp (CHIP, gadget->name)) {
-               printk (KERN_ERR "%s expected " CHIP " controller not %s\n",
-                       shortname, gadget->name);
+               printk (KERN_ERR "%s expected %s controller not %s\n",
+                       shortname, CHIP, gadget->name);
                return -ENODEV;
        }
 
@@ -1472,7 +1740,7 @@ gadgetfs_bind (struct usb_gadget *gadget)
        dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL);
        if (!dev->req)
                goto enomem;
-       dev->req->context = 0;
+       dev->req->context = NULL;
        dev->req->complete = epio_complete;
 
        if (activate_ep_files (dev) < 0)
@@ -1493,16 +1761,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
 {
        struct dev_data         *dev = get_gadget_data (gadget);
 
+       spin_lock (&dev->lock);
        if (dev->state == STATE_UNCONNECTED) {
                DBG (dev, "already unconnected\n");
-               return;
+               goto exit;
        }
        dev->state = STATE_UNCONNECTED;
 
        INFO (dev, "disconnected\n");
-       spin_lock (&dev->lock);
        next_event (dev, GADGETFS_DISCONNECT);
        ep0_readable (dev);
+exit:
        spin_unlock (&dev->lock);
 }
 
@@ -1527,7 +1796,7 @@ gadgetfs_suspend (struct usb_gadget *gadget)
 }
 
 static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        .speed          = USB_SPEED_HIGH,
 #else
        .speed          = USB_SPEED_FULL,
@@ -1541,14 +1810,31 @@ static struct usb_gadget_driver gadgetfs_driver = {
 
        .driver         = {
                .name           = (char *) shortname,
-               // .shutdown = ...
-               // .suspend = ...
-               // .resume = ...
        },
 };
 
 /*----------------------------------------------------------------------*/
 
+static void gadgetfs_nop(struct usb_gadget *arg) { }
+
+static int gadgetfs_probe (struct usb_gadget *gadget)
+{
+       CHIP = gadget->name;
+       return -EISNAM;
+}
+
+static struct usb_gadget_driver probe_driver = {
+       .speed          = USB_SPEED_HIGH,
+       .bind           = gadgetfs_probe,
+       .unbind         = gadgetfs_nop,
+       .setup          = (void *)gadgetfs_nop,
+       .disconnect     = gadgetfs_nop,
+       .driver         = {
+               .name           = "nop",
+       },
+};
+
+
 /* DEVICE INITIALIZATION
  *
  *     fd = open ("/dev/gadget/$CHIP", O_RDWR)
@@ -1574,7 +1860,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
  *
  * After initialization, the device stays active for as long as that
  * $CHIP file is open.  Events may then be read from that descriptor,
- * such configuration notifications.  More complex drivers will handle
+ * such as configuration notifications.  More complex drivers will handle
  * some control requests in user space.
  */
 
@@ -1585,11 +1871,12 @@ static int is_valid_config (struct usb_config_descriptor *config)
                && config->bConfigurationValue != 0
                && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0
                && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0;
+       /* FIXME if gadget->is_otg, _must_ include an otg descriptor */
        /* FIXME check lengths: walk to end */
 }
 
 static ssize_t
-dev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
+dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 {
        struct dev_data         *dev = fd->private_data;
        ssize_t                 value = len, length = len;
@@ -1611,7 +1898,7 @@ dev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
        buf += 4;
        length -= 4;
 
-       kbuf = kmalloc (length, SLAB_KERNEL);
+       kbuf = kmalloc (length, GFP_KERNEL);
        if (!kbuf)
                return -ENOMEM;
        if (copy_from_user (kbuf, buf, length)) {
@@ -1661,7 +1948,7 @@ dev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr)
        value = usb_gadget_register_driver (&gadgetfs_driver);
        if (value != 0) {
                kfree (dev->buf);
-               dev->buf = 0;
+               dev->buf = NULL;
        } else {
                /* at this point "good" hardware has for the first time
                 * let the USB the host see us.  alternatively, if users
@@ -1681,14 +1968,14 @@ fail:
        spin_unlock_irq (&dev->lock);
        pr_debug ("%s: %s fail %Zd, %p\n", shortname, __FUNCTION__, value, dev);
        kfree (dev->buf);
-       dev->buf = 0;
+       dev->buf = NULL;
        return value;
 }
 
 static int
 dev_open (struct inode *inode, struct file *fd)
 {
-       struct dev_data         *dev = inode->u.generic_ip;
+       struct dev_data         *dev = inode->i_private;
        int                     value = -EBUSY;
 
        if (dev->state == STATE_DEV_DISABLED) {
@@ -1701,8 +1988,10 @@ dev_open (struct inode *inode, struct file *fd)
        return value;
 }
 
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
        .owner =        THIS_MODULE,
+       .llseek =       no_llseek,
+
        .open =         dev_open,
        .write =        dev_config,
        .fasync =       ep0_fasync,
@@ -1734,7 +2023,7 @@ module_param (default_perm, uint, 0644);
 
 static struct inode *
 gadgetfs_make_inode (struct super_block *sb,
-               void *data, struct file_operations *fops,
+               void *data, const struct file_operations *fops,
                int mode)
 {
        struct inode *inode = new_inode (sb);
@@ -1743,11 +2032,10 @@ gadgetfs_make_inode (struct super_block *sb,
                inode->i_mode = mode;
                inode->i_uid = default_uid;
                inode->i_gid = default_gid;
-               inode->i_blksize = PAGE_CACHE_SIZE;
                inode->i_blocks = 0;
                inode->i_atime = inode->i_mtime = inode->i_ctime
                                = CURRENT_TIME;
-               inode->u.generic_ip = data;
+               inode->i_private = data;
                inode->i_fop = fops;
        }
        return inode;
@@ -1758,25 +2046,21 @@ gadgetfs_make_inode (struct super_block *sb,
  */
 static struct inode *
 gadgetfs_create_file (struct super_block *sb, char const *name,
-               void *data, struct file_operations *fops,
+               void *data, const struct file_operations *fops,
                struct dentry **dentry_p)
 {
        struct dentry   *dentry;
        struct inode    *inode;
-       struct qstr     qname;
 
-       qname.name = name;
-       qname.len = strlen (name);
-       qname.hash = full_name_hash (qname.name, qname.len);
-       dentry = d_alloc (sb->s_root, &qname);
+       dentry = d_alloc_name(sb->s_root, name);
        if (!dentry)
-               return 0;
+               return NULL;
 
        inode = gadgetfs_make_inode (sb, data, fops,
                        S_IFREG | (default_perm & S_IRWXUGO));
        if (!inode) {
                dput(dentry);
-               return 0;
+               return NULL;
        }
        d_add (dentry, inode);
        *dentry_p = dentry;
@@ -1798,23 +2082,27 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
        if (the_device)
                return -ESRCH;
 
+       /* fake probe to determine $CHIP */
+       (void) usb_gadget_register_driver (&probe_driver);
+       if (!CHIP)
+               return -ENODEV;
+
        /* superblock */
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = GADGETFS_MAGIC;
        sb->s_op = &gadget_fs_operations;
+       sb->s_time_gran = 1;
 
        /* root inode */
        inode = gadgetfs_make_inode (sb,
-                       0, &simple_dir_operations,
+                       NULL, &simple_dir_operations,
                        S_IFDIR | S_IRUGO | S_IXUGO);
        if (!inode)
-               return -ENOMEM;
+               goto enomem0;
        inode->i_op = &simple_dir_inode_operations;
-       if (!(d = d_alloc_root (inode))) {
-               iput (inode);
-               return -ENOMEM;
-       }
+       if (!(d = d_alloc_root (inode)))
+               goto enomem1;
        sb->s_root = d;
 
        /* the ep0 file is named after the controller we expect;
@@ -1822,29 +2110,36 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
         */
        dev = dev_new ();
        if (!dev)
-               return -ENOMEM;
+               goto enomem2;
 
        dev->sb = sb;
-       if (!(inode = gadgetfs_create_file (sb, CHIP,
+       if (!gadgetfs_create_file (sb, CHIP,
                                dev, &dev_init_operations,
-                               &dev->dentry))) {
-               put_dev(dev);
-               return -ENOMEM;
-       }
+                               &dev->dentry))
+               goto enomem3;
 
        /* other endpoint files are available after hardware setup,
         * from binding to a controller.
         */
        the_device = dev;
        return 0;
+
+enomem3:
+       put_dev (dev);
+enomem2:
+       dput (d);
+enomem1:
+       iput (inode);
+enomem0:
+       return -ENOMEM;
 }
 
 /* "mount -t gadgetfs path /dev/gadget" ends up here */
-static struct super_block *
+static int
 gadgetfs_get_sb (struct file_system_type *t, int flags,
-               const char *path, void *opts)
+               const char *path, void *opts, struct vfsmount *mnt)
 {
-       return get_sb_single (t, flags, opts, gadgetfs_fill_super);
+       return get_sb_single (t, flags, opts, gadgetfs_fill_super, mnt);
 }
 
 static void
@@ -1853,7 +2148,7 @@ gadgetfs_kill_sb (struct super_block *sb)
        kill_litter_super (sb);
        if (the_device) {
                put_dev (the_device);
-               the_device = 0;
+               the_device = NULL;
        }
 }