}
        case FFS_CLOSING:
                break;
+       case FFS_DEACTIVATED:
+               break;
        }
 
        mutex_unlock(&ffs->mutex);
        struct ffs_file_perms perms;
        umode_t root_mode;
        const char *dev_name;
+       bool no_disconnect;
        struct ffs_data *ffs_data;
 };
 
 
                /* Interpret option */
                switch (eq - opts) {
+               case 13:
+                       if (!memcmp(opts, "no_disconnect", 13))
+                               data->no_disconnect = !!value;
+                       else
+                               goto invalid;
+                       break;
                case 5:
                        if (!memcmp(opts, "rmode", 5))
                                data->root_mode  = (value & 0555) | S_IFDIR;
                        .gid = GLOBAL_ROOT_GID,
                },
                .root_mode = S_IFDIR | 0500,
+               .no_disconnect = false,
        };
        struct dentry *rv;
        int ret;
        if (unlikely(!ffs))
                return ERR_PTR(-ENOMEM);
        ffs->file_perms = data.perms;
+       ffs->no_disconnect = data.no_disconnect;
 
        ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
        if (unlikely(!ffs->dev_name)) {
        kill_litter_super(sb);
        if (sb->s_fs_info) {
                ffs_release_dev(sb->s_fs_info);
+               ffs_data_closed(sb->s_fs_info);
                ffs_data_put(sb->s_fs_info);
        }
 }
        ENTER();
 
        atomic_inc(&ffs->ref);
-       atomic_inc(&ffs->opened);
+       if (atomic_add_return(1, &ffs->opened) == 1 &&
+                       ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+               ffs_data_reset(ffs);
+       }
 }
 
 static void ffs_data_put(struct ffs_data *ffs)
        ENTER();
 
        if (atomic_dec_and_test(&ffs->opened)) {
+               if (ffs->no_disconnect) {
+                       ffs->state = FFS_DEACTIVATED;
+                       if (ffs->epfiles) {
+                               ffs_epfiles_destroy(ffs->epfiles,
+                                                  ffs->eps_count);
+                               ffs->epfiles = NULL;
+                       }
+                       if (ffs->setup_state == FFS_SETUP_PENDING)
+                               __ffs_ep0_stall(ffs);
+               } else {
+                       ffs->state = FFS_CLOSING;
+                       ffs_data_reset(ffs);
+               }
+       }
+       if (atomic_read(&ffs->opened) < 0) {
                ffs->state = FFS_CLOSING;
                ffs_data_reset(ffs);
        }
        kfree(epfiles);
 }
 
-
 static void ffs_func_eps_disable(struct ffs_function *func)
 {
        struct ffs_ep *ep         = func->eps;
                /* pending requests get nuked */
                if (likely(ep->ep))
                        usb_ep_disable(ep->ep);
-               epfile->ep = NULL;
-
                ++ep;
-               ++epfile;
+
+               if (epfile) {
+                       epfile->ep = NULL;
+                       ++epfile;
+               }
        } while (--count);
        spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 }
 
 /* Other USB function hooks *************************************************/
 
+static void ffs_reset_work(struct work_struct *work)
+{
+       struct ffs_data *ffs = container_of(work,
+               struct ffs_data, reset_work);
+       ffs_data_reset(ffs);
+}
+
 static int ffs_func_set_alt(struct usb_function *f,
                            unsigned interface, unsigned alt)
 {
        if (ffs->func)
                ffs_func_eps_disable(ffs->func);
 
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+               INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return -ENODEV;
+       }
+
        if (ffs->state != FFS_ACTIVE)
                return -ENODEV;
 
 
 #include <linux/usb/composite.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #ifdef VERBOSE_DEBUG
 #ifndef pr_vdebug
         */
        FFS_ACTIVE,
 
+       /*
+        * Function is visible to host, but it's not functional. All
+        * setup requests are stalled and transfers on another endpoints
+        * are refused. All epfiles, except ep0, are deleted so there
+        * is no way to perform any operations on them.
+        *
+        * This state is set after closing all functionfs files, when
+        * mount parameter "no_disconnect=1" has been set. Function will
+        * remain in deactivated state until filesystem is umounted or
+        * ep0 is opened again. In the second case functionfs state will
+        * be reset, and it will be ready for descriptors and strings
+        * writing.
+        *
+        * This is useful only when functionfs is composed to gadget
+        * with another function which can perform some critical
+        * operations, and it's strongly desired to have this operations
+        * completed, even after functionfs files closure.
+        */
+       FFS_DEACTIVATED,
+
        /*
         * All endpoints have been closed.  This state is also set if
         * we encounter an unrecoverable error.  The only
                kgid_t                          gid;
        }                               file_perms;
 
+       bool no_disconnect;
+       struct work_struct reset_work;
+
        /*
         * The endpoint files, filled by ffs_epfiles_create(),
         * destroyed by ffs_epfiles_destroy().