/*-------------------------------------------------------------------------*/
 
 struct fsg_dev;
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+       /* Callback function to call when thread exits.  If no
+        * callback is set or it returns value lower then zero MSF
+        * will force eject all LUNs it operates on (including those
+        * marked as non-removable or with prevent_medium_removal flag
+        * set). */
+       int (*thread_exits)(struct fsg_common *common);
+
+       /* Called prior to ejection.  Negative return means error,
+        * zero means to continue with ejection, positive means not to
+        * eject. */
+       int (*pre_eject)(struct fsg_common *common,
+                        struct fsg_lun *lun, int num);
+       /* Called after ejection.  Negative return means error, zero
+        * or positive is just a success. */
+       int (*post_eject)(struct fsg_common *common,
+                         struct fsg_lun *lun, int num);
+};
 
 
 /* Data shared by all the FSG instances. */
        struct completion       thread_notifier;
        struct task_struct      *thread_task;
 
-       /* Callback function to call when thread exits. */
-       int                     (*thread_exits)(struct fsg_common *common);
+       /* Callback functions. */
+       const struct fsg_operations     *ops;
        /* Gadget's private data. */
        void                    *private_data;
 
        const char              *lun_name_format;
        const char              *thread_name;
 
-       /* Callback function to call when thread exits.  If no
-        * callback is set or it returns value lower then zero MSF
-        * will force eject all LUNs it operates on (including those
-        * marked as non-removable or with prevent_medium_removal flag
-        * set). */
-       int                     (*thread_exits)(struct fsg_common *common);
+       /* Callback functions. */
+       const struct fsg_operations     *ops;
        /* Gadget's private data. */
        void                    *private_data;
 
        if (common->fsg)
                return 1;
        ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+       WARN_ON(1);
        return 0;
 }
 
        } else if (!curlun->removable) {
                curlun->sense_data = SS_INVALID_COMMAND;
                return -EINVAL;
-       }
-
-       loej = common->cmnd[4] & 0x02;
-       start = common->cmnd[4] & 0x01;
-
-       /* eject code from file_storage.c:do_start_stop() */
-
-       if ((common->cmnd[1] & ~0x01) != 0 ||     /* Mask away Immed */
-               (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+       } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+                  (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
                curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
                return -EINVAL;
        }
 
-       if (!start) {
-               /* Are we allowed to unload the media? */
-               if (curlun->prevent_medium_removal) {
-                       LDBG(curlun, "unload attempt prevented\n");
-                       curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
-                       return -EINVAL;
-               }
-               if (loej) {     /* Simulate an unload/eject */
-                       up_read(&common->filesem);
-                       down_write(&common->filesem);
-                       fsg_lun_close(curlun);
-                       up_write(&common->filesem);
-                       down_read(&common->filesem);
-               }
-       } else {
+       loej  = common->cmnd[4] & 0x02;
+       start = common->cmnd[4] & 0x01;
 
-               /* Our emulation doesn't support mounting; the medium is
-                * available for use as soon as it is loaded. */
+       /* Our emulation doesn't support mounting; the medium is
+        * available for use as soon as it is loaded. */
+       if (start) {
                if (!fsg_lun_is_open(curlun)) {
                        curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
                        return -EINVAL;
                }
+               return 0;
        }
-       return 0;
+
+       /* Are we allowed to unload the media? */
+       if (curlun->prevent_medium_removal) {
+               LDBG(curlun, "unload attempt prevented\n");
+               curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+               return -EINVAL;
+       }
+
+       if (!loej)
+               return 0;
+
+       /* Simulate an unload/eject */
+       if (common->ops && common->ops->pre_eject) {
+               int r = common->ops->pre_eject(common, curlun,
+                                              curlun - common->luns);
+               if (unlikely(r < 0))
+                       return r;
+               else if (r)
+                       return 0;
+       }
+
+       up_read(&common->filesem);
+       down_write(&common->filesem);
+       fsg_lun_close(curlun);
+       up_write(&common->filesem);
+       down_read(&common->filesem);
+
+       return common->ops && common->ops->post_eject
+               ? min(0, common->ops->post_eject(common, curlun,
+                                                curlun - common->luns))
+               : 0;
 }
 
 
        common->thread_task = NULL;
        spin_unlock_irq(&common->lock);
 
-       if (!common->thread_exits || common->thread_exits(common) < 0) {
+       if (!common->ops || !common->ops->thread_exits
+        || common->ops->thread_exits(common) < 0) {
                struct fsg_lun *curlun = common->luns;
                unsigned i = common->nluns;
 
                common->free_storage_on_release = 0;
        }
 
+       common->ops = cfg->ops;
        common->private_data = cfg->private_data;
 
        common->gadget = gadget;
 
 
        /* Tell the thread to start working */
-       common->thread_exits = cfg->thread_exits;
        common->thread_task =
                kthread_create(fsg_main_thread, common,
                               OR(cfg->thread_name, "file-storage"));
        cfg->product_name = 0;
        cfg->release = 0xffff;
 
-       cfg->thread_exits = 0;
-       cfg->private_data = 0;
+       cfg->ops = NULL;
+       cfg->private_data = NULL;
 
        /* Finalise */
        cfg->can_stall = params->stall;