Merge tag 'usb-for-v3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
[linux-2.6-microblaze.git] / drivers / usb / gadget / f_fs.c
index 2b43343..10c086a 100644 (file)
 #include <linux/usb/composite.h>
 #include <linux/usb/functionfs.h>
 
+#include <linux/aio.h>
+#include <linux/mmu_context.h>
+#include <linux/poll.h>
+
 #include "u_fs.h"
 #include "configfs.h"
 
@@ -99,6 +103,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
 }
 
 
+static inline enum ffs_setup_state
+ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
+{
+       return (enum ffs_setup_state)
+               cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
+}
+
+
 static void ffs_func_eps_disable(struct ffs_function *func);
 static int __must_check ffs_func_eps_enable(struct ffs_function *func);
 
@@ -122,8 +134,8 @@ struct ffs_ep {
        struct usb_ep                   *ep;    /* P: ffs->eps_lock */
        struct usb_request              *req;   /* P: epfile->mutex */
 
-       /* [0]: full speed, [1]: high speed */
-       struct usb_endpoint_descriptor  *descs[2];
+       /* [0]: full speed, [1]: high speed, [2]: super speed */
+       struct usb_endpoint_descriptor  *descs[3];
 
        u8                              num;
 
@@ -148,6 +160,25 @@ struct ffs_epfile {
        unsigned char                   _pad;
 };
 
+/*  ffs_io_data structure ***************************************************/
+
+struct ffs_io_data {
+       bool aio;
+       bool read;
+
+       struct kiocb *kiocb;
+       const struct iovec *iovec;
+       unsigned long nr_segs;
+       char __user *buf;
+       size_t len;
+
+       struct mm_struct *mm;
+       struct work_struct work;
+
+       struct usb_ep *ep;
+       struct usb_request *req;
+};
+
 static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
 static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
 
@@ -161,8 +192,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
 DEFINE_MUTEX(ffs_lock);
 EXPORT_SYMBOL(ffs_lock);
 
-static struct ffs_dev *ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_alloc_dev(void);
 static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
+static void _ffs_free_dev(struct ffs_dev *dev);
 static void *ffs_acquire_dev(const char *dev_name);
 static void ffs_release_dev(struct ffs_data *ffs_data);
 static int ffs_ready(struct ffs_data *ffs);
@@ -218,7 +251,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
        }
 
        ffs->setup_state = FFS_NO_SETUP;
-       return ffs->ep0req_status;
+       return req->status ? req->status : req->actual;
 }
 
 static int __ffs_ep0_stall(struct ffs_data *ffs)
@@ -244,7 +277,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
        ENTER();
 
        /* Fast check if setup was canceled */
-       if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
                return -EIDRM;
 
        /* Acquire mutex */
@@ -310,8 +343,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                 * rather then _irqsave
                 */
                spin_lock_irq(&ffs->ev.waitq.lock);
-               switch (FFS_SETUP_STATE(ffs)) {
-               case FFS_SETUP_CANCELED:
+               switch (ffs_setup_state_clear_cancelled(ffs)) {
+               case FFS_SETUP_CANCELLED:
                        ret = -EIDRM;
                        goto done_spin;
 
@@ -346,7 +379,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                /*
                 * We are guaranteed to be still in FFS_ACTIVE state
                 * but the state of setup could have changed from
-                * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+                * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
                 * to check for that.  If that happened we copied data
                 * from user space in vain but it's unlikely.
                 *
@@ -355,7 +388,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                 * transition can be performed and it's protected by
                 * mutex.
                 */
-               if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+               if (ffs_setup_state_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
                        ret = -EIDRM;
 done_spin:
                        spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -421,7 +455,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
        ENTER();
 
        /* Fast check if setup was canceled */
-       if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
                return -EIDRM;
 
        /* Acquire mutex */
@@ -441,8 +475,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
         */
        spin_lock_irq(&ffs->ev.waitq.lock);
 
-       switch (FFS_SETUP_STATE(ffs)) {
-       case FFS_SETUP_CANCELED:
+       switch (ffs_setup_state_clear_cancelled(ffs)) {
+       case FFS_SETUP_CANCELLED:
                ret = -EIDRM;
                break;
 
@@ -489,7 +523,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
                spin_lock_irq(&ffs->ev.waitq.lock);
 
                /* See ffs_ep0_write() */
-               if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+               if (ffs_setup_state_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
                        ret = -EIDRM;
                        break;
                }
@@ -558,6 +593,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
        return ret;
 }
 
+static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
+{
+       struct ffs_data *ffs = file->private_data;
+       unsigned int mask = POLLWRNORM;
+       int ret;
+
+       poll_wait(file, &ffs->ev.waitq, wait);
+
+       ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+       if (unlikely(ret < 0))
+               return mask;
+
+       switch (ffs->state) {
+       case FFS_READ_DESCRIPTORS:
+       case FFS_READ_STRINGS:
+               mask |= POLLOUT;
+               break;
+
+       case FFS_ACTIVE:
+               switch (ffs->setup_state) {
+               case FFS_NO_SETUP:
+                       if (ffs->ev.count)
+                               mask |= POLLIN;
+                       break;
+
+               case FFS_SETUP_PENDING:
+               case FFS_SETUP_CANCELLED:
+                       mask |= (POLLIN | POLLOUT);
+                       break;
+               }
+       case FFS_CLOSING:
+               break;
+       }
+
+       mutex_unlock(&ffs->mutex);
+
+       return mask;
+}
+
 static const struct file_operations ffs_ep0_operations = {
        .llseek =       no_llseek,
 
@@ -566,6 +640,7 @@ static const struct file_operations ffs_ep0_operations = {
        .read =         ffs_ep0_read,
        .release =      ffs_ep0_release,
        .unlocked_ioctl =       ffs_ep0_ioctl,
+       .poll =         ffs_ep0_poll,
 };
 
 
@@ -581,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
        }
 }
 
-static ssize_t ffs_epfile_io(struct file *file,
-                            char __user *buf, size_t len, int read)
+static void ffs_user_copy_worker(struct work_struct *work)
+{
+       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
+                                                  work);
+       int ret = io_data->req->status ? io_data->req->status :
+                                        io_data->req->actual;
+
+       if (io_data->read && ret > 0) {
+               int i;
+               size_t pos = 0;
+               use_mm(io_data->mm);
+               for (i = 0; i < io_data->nr_segs; i++) {
+                       if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
+                                                &io_data->buf[pos],
+                                                io_data->iovec[i].iov_len))) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       pos += io_data->iovec[i].iov_len;
+               }
+               unuse_mm(io_data->mm);
+       }
+
+       aio_complete(io_data->kiocb, ret, ret);
+
+       usb_ep_free_request(io_data->ep, io_data->req);
+
+       io_data->kiocb->private = NULL;
+       if (io_data->read)
+               kfree(io_data->iovec);
+       kfree(io_data->buf);
+       kfree(io_data);
+}
+
+static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
+                                        struct usb_request *req)
+{
+       struct ffs_io_data *io_data = req->context;
+
+       ENTER();
+
+       INIT_WORK(&io_data->work, ffs_user_copy_worker);
+       schedule_work(&io_data->work);
+}
+
+static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 {
        struct ffs_epfile *epfile = file->private_data;
        struct ffs_ep *ep;
@@ -612,7 +731,7 @@ static ssize_t ffs_epfile_io(struct file *file,
        }
 
        /* Do we halt? */
-       halt = !read == !epfile->in;
+       halt = (!io_data->read == !epfile->in);
        if (halt && epfile->isoc) {
                ret = -EINVAL;
                goto error;
@@ -630,15 +749,32 @@ static ssize_t ffs_epfile_io(struct file *file,
                 * Controller may require buffer size to be aligned to
                 * maxpacketsize of an out endpoint.
                 */
-               data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
+               data_len = io_data->read ?
+                          usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
+                          io_data->len;
 
                data = kmalloc(data_len, GFP_KERNEL);
                if (unlikely(!data))
                        return -ENOMEM;
-
-               if (!read && unlikely(copy_from_user(data, buf, len))) {
-                       ret = -EFAULT;
-                       goto error;
+               if (io_data->aio && !io_data->read) {
+                       int i;
+                       size_t pos = 0;
+                       for (i = 0; i < io_data->nr_segs; i++) {
+                               if (unlikely(copy_from_user(&data[pos],
+                                            io_data->iovec[i].iov_base,
+                                            io_data->iovec[i].iov_len))) {
+                                       ret = -EFAULT;
+                                       goto error;
+                               }
+                               pos += io_data->iovec[i].iov_len;
+                       }
+               } else {
+                       if (!io_data->read &&
+                           unlikely(__copy_from_user(data, io_data->buf,
+                                                     io_data->len))) {
+                               ret = -EFAULT;
+                               goto error;
+                       }
                }
        }
 
@@ -661,40 +797,74 @@ static ssize_t ffs_epfile_io(struct file *file,
                ret = -EBADMSG;
        } else {
                /* Fire the request */
-               DECLARE_COMPLETION_ONSTACK(done);
+               struct usb_request *req;
 
-               struct usb_request *req = ep->req;
-               req->context  = &done;
-               req->complete = ffs_epfile_io_complete;
-               req->buf      = data;
-               req->length   = data_len;
+               if (io_data->aio) {
+                       req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
+                       if (unlikely(!req))
+                               goto error;
 
-               ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+                       req->buf      = data;
+                       req->length   = io_data->len;
 
-               spin_unlock_irq(&epfile->ffs->eps_lock);
+                       io_data->buf = data;
+                       io_data->ep = ep->ep;
+                       io_data->req = req;
 
-               if (unlikely(ret < 0)) {
-                       /* nop */
-               } else if (unlikely(wait_for_completion_interruptible(&done))) {
-                       ret = -EINTR;
-                       usb_ep_dequeue(ep->ep, req);
+                       req->context  = io_data;
+                       req->complete = ffs_epfile_async_io_complete;
+
+                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+                       if (unlikely(ret)) {
+                               usb_ep_free_request(ep->ep, req);
+                               goto error;
+                       }
+                       ret = -EIOCBQUEUED;
+
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
                } else {
-                       /*
-                        * XXX We may end up silently droping data here.
-                        * Since data_len (i.e. req->length) may be bigger
-                        * than len (after being rounded up to maxpacketsize),
-                        * we may end up with more data then user space has
-                        * space for.
-                        */
-                       ret = ep->status;
-                       if (read && ret > 0 &&
-                           unlikely(copy_to_user(buf, data,
-                                                 min_t(size_t, ret, len))))
-                               ret = -EFAULT;
+                       DECLARE_COMPLETION_ONSTACK(done);
+
+                       req = ep->req;
+                       req->buf      = data;
+                       req->length   = io_data->len;
+
+                       req->context  = &done;
+                       req->complete = ffs_epfile_io_complete;
+
+                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
+
+                       if (unlikely(ret < 0)) {
+                               /* nop */
+                       } else if (unlikely(
+                                  wait_for_completion_interruptible(&done))) {
+                               ret = -EINTR;
+                               usb_ep_dequeue(ep->ep, req);
+                       } else {
+                               /*
+                                * XXX We may end up silently droping data
+                                * here.  Since data_len (i.e. req->length) may
+                                * be bigger than len (after being rounded up
+                                * to maxpacketsize), we may end up with more
+                                * data then user space has space for.
+                                */
+                               ret = ep->status;
+                               if (io_data->read && ret > 0) {
+                                       ret = min_t(size_t, ret, io_data->len);
+
+                                       if (unlikely(copy_to_user(io_data->buf,
+                                               data, ret)))
+                                               ret = -EFAULT;
+                               }
+                       }
+                       kfree(data);
                }
        }
 
        mutex_unlock(&epfile->mutex);
+       return ret;
 error:
        kfree(data);
        return ret;
@@ -704,17 +874,31 @@ static ssize_t
 ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
                 loff_t *ptr)
 {
+       struct ffs_io_data io_data;
+
        ENTER();
 
-       return ffs_epfile_io(file, (char __user *)buf, len, 0);
+       io_data.aio = false;
+       io_data.read = false;
+       io_data.buf = (char * __user)buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
 }
 
 static ssize_t
 ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
 {
+       struct ffs_io_data io_data;
+
        ENTER();
 
-       return ffs_epfile_io(file, buf, len, 1);
+       io_data.aio = false;
+       io_data.read = true;
+       io_data.buf = buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
 }
 
 static int
@@ -733,6 +917,89 @@ ffs_epfile_open(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int ffs_aio_cancel(struct kiocb *kiocb)
+{
+       struct ffs_io_data *io_data = kiocb->private;
+       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
+       int value;
+
+       ENTER();
+
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (likely(io_data && io_data->ep && io_data->req))
+               value = usb_ep_dequeue(io_data->ep, io_data->req);
+       else
+               value = -EINVAL;
+
+       spin_unlock_irq(&epfile->ffs->eps_lock);
+
+       return value;
+}
+
+static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
+                                   const struct iovec *iovec,
+                                   unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+
+       ENTER();
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data))
+               return -ENOMEM;
+
+       io_data->aio = true;
+       io_data->read = false;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
+static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
+                                  const struct iovec *iovec,
+                                  unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+       struct iovec *iovec_copy;
+
+       ENTER();
+
+       iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
+       if (unlikely(!iovec_copy))
+               return -ENOMEM;
+
+       memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data)) {
+               kfree(iovec_copy);
+               return -ENOMEM;
+       }
+
+       io_data->aio = true;
+       io_data->read = true;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec_copy;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
 static int
 ffs_epfile_release(struct inode *inode, struct file *file)
 {
@@ -789,6 +1056,8 @@ static const struct file_operations ffs_epfile_operations = {
        .open =         ffs_epfile_open,
        .write =        ffs_epfile_write,
        .read =         ffs_epfile_read,
+       .aio_write =    ffs_epfile_aio_write,
+       .aio_read =     ffs_epfile_aio_read,
        .release =      ffs_epfile_release,
        .unlocked_ioctl =       ffs_epfile_ioctl,
 };
@@ -1172,7 +1441,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
        if (ffs->epfiles)
                ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
 
-       kfree(ffs->raw_descs);
+       kfree(ffs->raw_descs_data);
        kfree(ffs->raw_strings);
        kfree(ffs->stringtabs);
 }
@@ -1184,14 +1453,15 @@ static void ffs_data_reset(struct ffs_data *ffs)
        ffs_data_clear(ffs);
 
        ffs->epfiles = NULL;
+       ffs->raw_descs_data = NULL;
        ffs->raw_descs = NULL;
        ffs->raw_strings = NULL;
        ffs->stringtabs = NULL;
 
        ffs->raw_descs_length = 0;
-       ffs->raw_fs_descs_length = 0;
        ffs->fs_descs_count = 0;
        ffs->hs_descs_count = 0;
+       ffs->ss_descs_count = 0;
 
        ffs->strings_count = 0;
        ffs->interfaces_count = 0;
@@ -1334,7 +1604,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
        spin_lock_irqsave(&func->ffs->eps_lock, flags);
        do {
                struct usb_endpoint_descriptor *ds;
-               ds = ep->descs[ep->descs[1] ? 1 : 0];
+               int desc_idx;
+
+               if (ffs->gadget->speed == USB_SPEED_SUPER)
+                       desc_idx = 2;
+               else if (ffs->gadget->speed == USB_SPEED_HIGH)
+                       desc_idx = 1;
+               else
+                       desc_idx = 0;
+
+               /* fall-back to lower speed if desc missing for current speed */
+               do {
+                       ds = ep->descs[desc_idx];
+               } while (!ds && --desc_idx >= 0);
+
+               if (!ds) {
+                       ret = -EINVAL;
+                       break;
+               }
 
                ep->ep->driver_data = ep;
                ep->ep->desc = ds;
@@ -1469,6 +1756,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
        }
                break;
 
+       case USB_DT_SS_ENDPOINT_COMP:
+               pr_vdebug("EP SS companion descriptor\n");
+               if (length != sizeof(struct usb_ss_ep_comp_descriptor))
+                       goto inv_length;
+               break;
+
        case USB_DT_OTHER_SPEED_CONFIG:
        case USB_DT_INTERFACE_POWER:
        case USB_DT_DEBUG:
@@ -1579,60 +1872,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
 static int __ffs_data_got_descs(struct ffs_data *ffs,
                                char *const _data, size_t len)
 {
-       unsigned fs_count, hs_count;
-       int fs_len, ret = -EINVAL;
-       char *data = _data;
+       char *data = _data, *raw_descs;
+       unsigned counts[3], flags;
+       int ret = -EINVAL, i;
 
        ENTER();
 
-       if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
-                    get_unaligned_le32(data + 4) != len))
+       if (get_unaligned_le32(data + 4) != len)
                goto error;
-       fs_count = get_unaligned_le32(data +  8);
-       hs_count = get_unaligned_le32(data + 12);
-
-       if (!fs_count && !hs_count)
-               goto einval;
 
-       data += 16;
-       len  -= 16;
-
-       if (likely(fs_count)) {
-               fs_len = ffs_do_descs(fs_count, data, len,
-                                     __ffs_data_do_entity, ffs);
-               if (unlikely(fs_len < 0)) {
-                       ret = fs_len;
+       switch (get_unaligned_le32(data)) {
+       case FUNCTIONFS_DESCRIPTORS_MAGIC:
+               flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+               data += 8;
+               len  -= 8;
+               break;
+       case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+               flags = get_unaligned_le32(data + 8);
+               if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
+                             FUNCTIONFS_HAS_HS_DESC |
+                             FUNCTIONFS_HAS_SS_DESC)) {
+                       ret = -ENOSYS;
                        goto error;
                }
+               data += 12;
+               len  -= 12;
+               break;
+       default:
+               goto error;
+       }
 
-               data += fs_len;
-               len  -= fs_len;
-       } else {
-               fs_len = 0;
+       /* Read fs_count, hs_count and ss_count (if present) */
+       for (i = 0; i < 3; ++i) {
+               if (!(flags & (1 << i))) {
+                       counts[i] = 0;
+               } else if (len < 4) {
+                       goto error;
+               } else {
+                       counts[i] = get_unaligned_le32(data);
+                       data += 4;
+                       len  -= 4;
+               }
        }
 
-       if (likely(hs_count)) {
-               ret = ffs_do_descs(hs_count, data, len,
+       /* Read descriptors */
+       raw_descs = data;
+       for (i = 0; i < 3; ++i) {
+               if (!counts[i])
+                       continue;
+               ret = ffs_do_descs(counts[i], data, len,
                                   __ffs_data_do_entity, ffs);
-               if (unlikely(ret < 0))
+               if (ret < 0)
                        goto error;
-       } else {
-               ret = 0;
+               data += ret;
+               len  -= ret;
        }
 
-       if (unlikely(len != ret))
-               goto einval;
+       if (raw_descs == data || len) {
+               ret = -EINVAL;
+               goto error;
+       }
 
-       ffs->raw_fs_descs_length = fs_len;
-       ffs->raw_descs_length    = fs_len + ret;
-       ffs->raw_descs           = _data;
-       ffs->fs_descs_count      = fs_count;
-       ffs->hs_descs_count      = hs_count;
+       ffs->raw_descs_data     = _data;
+       ffs->raw_descs          = raw_descs;
+       ffs->raw_descs_length   = data - raw_descs;
+       ffs->fs_descs_count     = counts[0];
+       ffs->hs_descs_count     = counts[1];
+       ffs->ss_descs_count     = counts[2];
 
        return 0;
 
-einval:
-       ret = -EINVAL;
 error:
        kfree(_data);
        return ret;
@@ -1789,7 +2098,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
         * the source does nothing.
         */
        if (ffs->setup_state == FFS_SETUP_PENDING)
-               ffs->setup_state = FFS_SETUP_CANCELED;
+               ffs->setup_state = FFS_SETUP_CANCELLED;
 
        switch (type) {
        case FUNCTIONFS_RESUME:
@@ -1850,21 +2159,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
        struct usb_endpoint_descriptor *ds = (void *)desc;
        struct ffs_function *func = priv;
        struct ffs_ep *ffs_ep;
-
-       /*
-        * If hs_descriptors is not NULL then we are reading hs
-        * descriptors now
-        */
-       const int isHS = func->function.hs_descriptors != NULL;
-       unsigned idx;
+       unsigned ep_desc_id, idx;
+       static const char *speed_names[] = { "full", "high", "super" };
 
        if (type != FFS_DESCRIPTOR)
                return 0;
 
-       if (isHS)
+       /*
+        * If ss_descriptors is not NULL, we are reading super speed
+        * descriptors; if hs_descriptors is not NULL, we are reading high
+        * speed descriptors; otherwise, we are reading full speed
+        * descriptors.
+        */
+       if (func->function.ss_descriptors) {
+               ep_desc_id = 2;
+               func->function.ss_descriptors[(long)valuep] = desc;
+       } else if (func->function.hs_descriptors) {
+               ep_desc_id = 1;
                func->function.hs_descriptors[(long)valuep] = desc;
-       else
+       } else {
+               ep_desc_id = 0;
                func->function.fs_descriptors[(long)valuep]    = desc;
+       }
 
        if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
                return 0;
@@ -1872,13 +2188,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
        idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
        ffs_ep = func->eps + idx;
 
-       if (unlikely(ffs_ep->descs[isHS])) {
-               pr_vdebug("two %sspeed descriptors for EP %d\n",
-                         isHS ? "high" : "full",
+       if (unlikely(ffs_ep->descs[ep_desc_id])) {
+               pr_err("two %sspeed descriptors for EP %d\n",
+                         speed_names[ep_desc_id],
                          ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
                return -EINVAL;
        }
-       ffs_ep->descs[isHS] = ds;
+       ffs_ep->descs[ep_desc_id] = ds;
 
        ffs_dump_mem(": Original  ep desc", ds, ds->bLength);
        if (ffs_ep->ep) {
@@ -2022,8 +2338,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
        const int full = !!func->ffs->fs_descs_count;
        const int high = gadget_is_dualspeed(func->gadget) &&
                func->ffs->hs_descs_count;
+       const int super = gadget_is_superspeed(func->gadget) &&
+               func->ffs->ss_descs_count;
 
-       int ret;
+       int fs_len, hs_len, ret;
 
        /* Make it a single chunk, less management later on */
        vla_group(d);
@@ -2032,15 +2350,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
                full ? ffs->fs_descs_count + 1 : 0);
        vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
                high ? ffs->hs_descs_count + 1 : 0);
+       vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
+               super ? ffs->ss_descs_count + 1 : 0);
        vla_item_with_sz(d, short, inums, ffs->interfaces_count);
-       vla_item_with_sz(d, char, raw_descs,
-               high ? ffs->raw_descs_length : ffs->raw_fs_descs_length);
+       vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
        char *vlabuf;
 
        ENTER();
 
-       /* Only high speed but not supported by gadget? */
-       if (unlikely(!(full | high)))
+       /* Has descriptors only for speeds gadget does not support */
+       if (unlikely(!(full | high | super)))
                return -ENOTSUPP;
 
        /* Allocate a single chunk, less management later on */
@@ -2050,8 +2369,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
 
        /* Zero */
        memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
-       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
-              d_raw_descs__sz);
+       /* Copy descriptors  */
+       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
+              ffs->raw_descs_length);
+
        memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
        for (ret = ffs->eps_count; ret; --ret) {
                struct ffs_ep *ptr;
@@ -2073,22 +2394,38 @@ static int _ffs_func_bind(struct usb_configuration *c,
         */
        if (likely(full)) {
                func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
-               ret = ffs_do_descs(ffs->fs_descs_count,
-                                  vla_ptr(vlabuf, d, raw_descs),
-                                  d_raw_descs__sz,
-                                  __ffs_func_bind_do_descs, func);
-               if (unlikely(ret < 0))
+               fs_len = ffs_do_descs(ffs->fs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs),
+                                     d_raw_descs__sz,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(fs_len < 0)) {
+                       ret = fs_len;
                        goto error;
+               }
        } else {
-               ret = 0;
+               fs_len = 0;
        }
 
        if (likely(high)) {
                func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
-               ret = ffs_do_descs(ffs->hs_descs_count,
-                                  vla_ptr(vlabuf, d, raw_descs) + ret,
-                                  d_raw_descs__sz - ret,
-                                  __ffs_func_bind_do_descs, func);
+               hs_len = ffs_do_descs(ffs->hs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs) + fs_len,
+                                     d_raw_descs__sz - fs_len,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(hs_len < 0)) {
+                       ret = hs_len;
+                       goto error;
+               }
+       } else {
+               hs_len = 0;
+       }
+
+       if (likely(super)) {
+               func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
+               ret = ffs_do_descs(ffs->ss_descs_count,
+                               vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
+                               d_raw_descs__sz - fs_len - hs_len,
+                               __ffs_func_bind_do_descs, func);
                if (unlikely(ret < 0))
                        goto error;
        }
@@ -2099,7 +2436,8 @@ static int _ffs_func_bind(struct usb_configuration *c,
         * now.
         */
        ret = ffs_do_descs(ffs->fs_descs_count +
-                          (high ? ffs->hs_descs_count : 0),
+                          (high ? ffs->hs_descs_count : 0) +
+                          (super ? ffs->ss_descs_count : 0),
                           vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
                           __ffs_func_bind_do_nums, func);
        if (unlikely(ret < 0))
@@ -2258,7 +2596,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
 
 static LIST_HEAD(ffs_devices);
 
-static struct ffs_dev *_ffs_find_dev(const char *name)
+static struct ffs_dev *_ffs_do_find_dev(const char *name)
 {
        struct ffs_dev *dev;
 
@@ -2275,7 +2613,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-static struct ffs_dev *ffs_get_single_dev(void)
+static struct ffs_dev *_ffs_get_single_dev(void)
 {
        struct ffs_dev *dev;
 
@@ -2291,15 +2629,15 @@ static struct ffs_dev *ffs_get_single_dev(void)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-static struct ffs_dev *ffs_find_dev(const char *name)
+static struct ffs_dev *_ffs_find_dev(const char *name)
 {
        struct ffs_dev *dev;
 
-       dev = ffs_get_single_dev();
+       dev = _ffs_get_single_dev();
        if (dev)
                return dev;
 
-       return _ffs_find_dev(name);
+       return _ffs_do_find_dev(name);
 }
 
 /* Configfs support *********************************************************/
@@ -2335,7 +2673,7 @@ static void ffs_free_inst(struct usb_function_instance *f)
 
        opts = to_f_fs_opts(f);
        ffs_dev_lock();
-       ffs_free_dev(opts->dev);
+       _ffs_free_dev(opts->dev);
        ffs_dev_unlock();
        kfree(opts);
 }
@@ -2390,7 +2728,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
        opts->func_inst.set_inst_name = ffs_set_inst_name;
        opts->func_inst.free_func_inst = ffs_free_inst;
        ffs_dev_lock();
-       dev = ffs_alloc_dev();
+       dev = _ffs_alloc_dev();
        ffs_dev_unlock();
        if (IS_ERR(dev)) {
                kfree(opts);
@@ -2446,6 +2784,7 @@ static void ffs_func_unbind(struct usb_configuration *c,
         */
        func->function.fs_descriptors = NULL;
        func->function.hs_descriptors = NULL;
+       func->function.ss_descriptors = NULL;
        func->interfaces_nums = NULL;
 
        ffs_event_add(ffs, FUNCTIONFS_UNBIND);
@@ -2478,12 +2817,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-struct ffs_dev *ffs_alloc_dev(void)
+static struct ffs_dev *_ffs_alloc_dev(void)
 {
        struct ffs_dev *dev;
        int ret;
 
-       if (ffs_get_single_dev())
+       if (_ffs_get_single_dev())
                        return ERR_PTR(-EBUSY);
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -2511,10 +2850,10 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
 {
        struct ffs_dev *existing;
 
-       existing = _ffs_find_dev(name);
+       existing = _ffs_do_find_dev(name);
        if (existing)
                return -EBUSY;
-       
+
        dev->name = name;
 
        return 0;
@@ -2555,7 +2894,7 @@ EXPORT_SYMBOL(ffs_single_dev);
 /*
  * ffs_lock must be taken by the caller of this function
  */
-void ffs_free_dev(struct ffs_dev *dev)
+static void _ffs_free_dev(struct ffs_dev *dev)
 {
        list_del(&dev->entry);
        if (dev->name_allocated)
@@ -2572,7 +2911,7 @@ static void *ffs_acquire_dev(const char *dev_name)
        ENTER();
        ffs_dev_lock();
 
-       ffs_dev = ffs_find_dev(dev_name);
+       ffs_dev = _ffs_find_dev(dev_name);
        if (!ffs_dev)
                ffs_dev = ERR_PTR(-ENODEV);
        else if (ffs_dev->mounted)
@@ -2595,11 +2934,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
        ffs_dev_lock();
 
        ffs_dev = ffs_data->private_data;
-       if (ffs_dev)
+       if (ffs_dev) {
                ffs_dev->mounted = false;
-       
-       if (ffs_dev->ffs_release_dev_callback)
-               ffs_dev->ffs_release_dev_callback(ffs_dev);
+
+               if (ffs_dev->ffs_release_dev_callback)
+                       ffs_dev->ffs_release_dev_callback(ffs_dev);
+       }
 
        ffs_dev_unlock();
 }