|
@@ -605,6 +605,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
|
|
|
}
|
|
|
case FFS_CLOSING:
|
|
|
break;
|
|
|
+ case FFS_DEACTIVATED:
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&ffs->mutex);
|
|
@@ -1179,6 +1181,7 @@ struct ffs_sb_fill_data {
|
|
|
struct ffs_file_perms perms;
|
|
|
umode_t root_mode;
|
|
|
const char *dev_name;
|
|
|
+ bool no_disconnect;
|
|
|
struct ffs_data *ffs_data;
|
|
|
};
|
|
|
|
|
@@ -1249,6 +1252,12 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
|
|
|
|
|
|
/* 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;
|
|
@@ -1313,6 +1322,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
|
|
|
.gid = GLOBAL_ROOT_GID,
|
|
|
},
|
|
|
.root_mode = S_IFDIR | 0500,
|
|
|
+ .no_disconnect = false,
|
|
|
};
|
|
|
struct dentry *rv;
|
|
|
int ret;
|
|
@@ -1329,6 +1339,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
|
|
|
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)) {
|
|
@@ -1360,6 +1371,7 @@ ffs_fs_kill_sb(struct super_block *sb)
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
@@ -1416,7 +1428,11 @@ static void ffs_data_opened(struct ffs_data *ffs)
|
|
|
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)
|
|
@@ -1438,6 +1454,21 @@ static void ffs_data_closed(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);
|
|
|
}
|
|
@@ -1615,7 +1646,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
|
|
|
kfree(epfiles);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static void ffs_func_eps_disable(struct ffs_function *func)
|
|
|
{
|
|
|
struct ffs_ep *ep = func->eps;
|
|
@@ -1628,10 +1658,12 @@ static void ffs_func_eps_disable(struct ffs_function *func)
|
|
|
/* 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);
|
|
|
}
|
|
@@ -2894,6 +2926,13 @@ static int ffs_func_bind(struct usb_configuration *c,
|
|
|
|
|
|
/* 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)
|
|
|
{
|
|
@@ -2910,6 +2949,13 @@ static int ffs_func_set_alt(struct usb_function *f,
|
|
|
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;
|
|
|
|