linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] usb: gadget: f_fs: add "no_disconnect" mode
@ 2014-10-09 11:47 Robert Baldyga
  2014-10-09 13:57 ` Michal Nazarewicz
  0 siblings, 1 reply; 3+ messages in thread
From: Robert Baldyga @ 2014-10-09 11:47 UTC (permalink / raw)
  To: balbi
  Cc: gregkh, linux-usb, linux-kernel, mina86, andrzej.p, m.szyprowski,
	k.opasiak, Robert Baldyga

Since we can compose gadgets from many functions, there is the problem
related to gadget breakage while FunctionFS daemon being closed. FFS
function is userspace code so there is no way to know when it will close
files (it doesn't matter what is the reason of this situation, it can
be daemon logic, program breakage, process kill or any other). So when
we have another function in gadget which, for example, sends some amount
of data, does some software update or implements some real-time functionality,
we may want to keep the gadget connected despite FFS function is no longer
functional.

We can't just remove one of functions from gadget since it has been
enumerated, so the only way to keep entire gadget working is to make
broken FFS function deactivated but still visible to host. For this
purpose this patch introduces "no_disconnect" mode. It can be enabled
by setting mount option "no_disconnect=1", and results with defering
function disconnect to the moment of reopen ep0 file or filesystem
unmount. After closing all endpoint files, FunctionFS is set to state
FFS_DEACTIVATED.

When ffs->state == FFS_DEACTIVATED:
- function is still bound and visible to host,
- setup requests are automatically stalled,
- transfers on other endpoints are refused,
- epfiles, except ep0, are deleted from the filesystem,
- opening ep0 causes the function to be closes, and then FunctionFS 
  is ready for descriptors and string write,
- unmounting of the FunctionFS instance causes the function to be closed.

Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
---

Changelog:

v3:
- change option name to more descriptive and less scary,
- fix cleaning up on unmount (call ffs_data_closed() in ffs_fs_kill_sb(),
  and ffs_data_clear() in ffs_data_closed() if ffs->opened is negative).

v2: https://lkml.org/lkml/2014/10/7/109
- delete epfiles, excepting ep0, when FFS is in "zombie" mode,
- add description of FFS_ZOMBIE state,
- minor cleanups.

v1: https://lkml.org/lkml/2014/10/6/128

 drivers/usb/gadget/function/f_fs.c | 42 +++++++++++++++++++++++++++++++-------
 drivers/usb/gadget/function/u_fs.h | 22 ++++++++++++++++++++
 2 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 12dbdaf..f326267 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -606,6 +606,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);
@@ -1152,6 +1154,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;
 };
 
@@ -1222,6 +1225,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;
@@ -1286,6 +1295,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;
@@ -1302,6 +1312,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)) {
@@ -1333,6 +1344,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);
 	}
 }
@@ -1389,7 +1401,9 @@ 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)
+		if (ffs->state == FFS_DEACTIVATED)
+			ffs_data_reset(ffs);
 }
 
 static void ffs_data_put(struct ffs_data *ffs)
@@ -1411,9 +1425,22 @@ static void ffs_data_closed(struct ffs_data *ffs)
 	ENTER();
 
 	if (atomic_dec_and_test(&ffs->opened)) {
-		ffs->state = FFS_CLOSING;
-		ffs_data_reset(ffs);
+		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_data_clear(ffs);
 
 	ffs_data_put(ffs);
 }
@@ -1588,7 +1615,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;
@@ -1601,10 +1627,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);
 }
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index cd128e3..7bc0ca2 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -93,6 +93,26 @@ enum ffs_state {
 	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
 	 * unrecoverable error is situation when after reading strings
@@ -251,6 +271,8 @@ struct ffs_data {
 		kgid_t				gid;
 	}				file_perms;
 
+	bool no_disconnect;
+
 	/*
 	 * The endpoint files, filled by ffs_epfiles_create(),
 	 * destroyed by ffs_epfiles_destroy().
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v3] usb: gadget: f_fs: add "no_disconnect" mode
  2014-10-09 11:47 [PATCH v3] usb: gadget: f_fs: add "no_disconnect" mode Robert Baldyga
@ 2014-10-09 13:57 ` Michal Nazarewicz
  2014-10-10  8:10   ` Robert Baldyga
  0 siblings, 1 reply; 3+ messages in thread
From: Michal Nazarewicz @ 2014-10-09 13:57 UTC (permalink / raw)
  To: Robert Baldyga, balbi
  Cc: gregkh, linux-usb, linux-kernel, andrzej.p, m.szyprowski,
	k.opasiak, Robert Baldyga

On Thu, Oct 09 2014, Robert Baldyga <r.baldyga@samsung.com> wrote:
> Since we can compose gadgets from many functions, there is the problem
> related to gadget breakage while FunctionFS daemon being closed. FFS
> function is userspace code so there is no way to know when it will close
> files (it doesn't matter what is the reason of this situation, it can
> be daemon logic, program breakage, process kill or any other). So when
> we have another function in gadget which, for example, sends some amount
> of data, does some software update or implements some real-time functionality,
> we may want to keep the gadget connected despite FFS function is no longer
> functional.
>
> We can't just remove one of functions from gadget since it has been
> enumerated, so the only way to keep entire gadget working is to make
> broken FFS function deactivated but still visible to host. For this
> purpose this patch introduces "no_disconnect" mode. It can be enabled
> by setting mount option "no_disconnect=1", and results with defering
> function disconnect to the moment of reopen ep0 file or filesystem
> unmount. After closing all endpoint files, FunctionFS is set to state
> FFS_DEACTIVATED.
>
> When ffs->state == FFS_DEACTIVATED:
> - function is still bound and visible to host,
> - setup requests are automatically stalled,
> - transfers on other endpoints are refused,
> - epfiles, except ep0, are deleted from the filesystem,
> - opening ep0 causes the function to be closes, and then FunctionFS 
>   is ready for descriptors and string write,
> - unmounting of the FunctionFS instance causes the function to be closed.
>
> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>

Acked-by: Michal Nazarewicz <mina86@mina86.com>

Even though I have some concerns whether the mode should be enable via
a mount option.  Perhaps it should come from the gadget or the daemon?
I don't have strong feelings though, so I would be fine with the code as
is.

> ---
>
> Changelog:
>
> v3:
> - change option name to more descriptive and less scary,
> - fix cleaning up on unmount (call ffs_data_closed() in ffs_fs_kill_sb(),
>   and ffs_data_clear() in ffs_data_closed() if ffs->opened is negative).
>
> v2: https://lkml.org/lkml/2014/10/7/109
> - delete epfiles, excepting ep0, when FFS is in "zombie" mode,
> - add description of FFS_ZOMBIE state,
> - minor cleanups.
>
> v1: https://lkml.org/lkml/2014/10/6/128
>
>  drivers/usb/gadget/function/f_fs.c | 42 +++++++++++++++++++++++++++++++-------
>  drivers/usb/gadget/function/u_fs.h | 22 ++++++++++++++++++++
>  2 files changed, 57 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
> index 12dbdaf..f326267 100644
> --- a/drivers/usb/gadget/function/f_fs.c
> +++ b/drivers/usb/gadget/function/f_fs.c
> @@ -606,6 +606,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);
> @@ -1152,6 +1154,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;
>  };
>  
> @@ -1222,6 +1225,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;
> @@ -1286,6 +1295,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;
> @@ -1302,6 +1312,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)) {
> @@ -1333,6 +1344,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);
>  	}
>  }
> @@ -1389,7 +1401,9 @@ 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)
> +		if (ffs->state == FFS_DEACTIVATED)
> +			ffs_data_reset(ffs);
>  }
>  
>  static void ffs_data_put(struct ffs_data *ffs)
> @@ -1411,9 +1425,22 @@ static void ffs_data_closed(struct ffs_data *ffs)
>  	ENTER();
>  
>  	if (atomic_dec_and_test(&ffs->opened)) {
> -		ffs->state = FFS_CLOSING;
> -		ffs_data_reset(ffs);
> +		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_data_clear(ffs);
>  
>  	ffs_data_put(ffs);
>  }
> @@ -1588,7 +1615,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;
> @@ -1601,10 +1627,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);
>  }
> diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
> index cd128e3..7bc0ca2 100644
> --- a/drivers/usb/gadget/function/u_fs.h
> +++ b/drivers/usb/gadget/function/u_fs.h
> @@ -93,6 +93,26 @@ enum ffs_state {
>  	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
>  	 * unrecoverable error is situation when after reading strings
> @@ -251,6 +271,8 @@ struct ffs_data {
>  		kgid_t				gid;
>  	}				file_perms;
>  
> +	bool no_disconnect;
> +
>  	/*
>  	 * The endpoint files, filled by ffs_epfiles_create(),
>  	 * destroyed by ffs_epfiles_destroy().
> -- 
> 1.9.1
>

-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<mpn@google.com>--<xmpp:mina86@jabber.org>--ooO--(_)--Ooo--

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v3] usb: gadget: f_fs: add "no_disconnect" mode
  2014-10-09 13:57 ` Michal Nazarewicz
@ 2014-10-10  8:10   ` Robert Baldyga
  0 siblings, 0 replies; 3+ messages in thread
From: Robert Baldyga @ 2014-10-10  8:10 UTC (permalink / raw)
  To: Michal Nazarewicz, balbi
  Cc: gregkh, linux-usb, linux-kernel, andrzej.p, m.szyprowski,
	k.opasiak

On 10/09/2014 03:57 PM, Michal Nazarewicz wrote:
> On Thu, Oct 09 2014, Robert Baldyga <r.baldyga@samsung.com> wrote:
>> Since we can compose gadgets from many functions, there is the problem
>> related to gadget breakage while FunctionFS daemon being closed. FFS
>> function is userspace code so there is no way to know when it will close
>> files (it doesn't matter what is the reason of this situation, it can
>> be daemon logic, program breakage, process kill or any other). So when
>> we have another function in gadget which, for example, sends some amount
>> of data, does some software update or implements some real-time functionality,
>> we may want to keep the gadget connected despite FFS function is no longer
>> functional.
>>
>> We can't just remove one of functions from gadget since it has been
>> enumerated, so the only way to keep entire gadget working is to make
>> broken FFS function deactivated but still visible to host. For this
>> purpose this patch introduces "no_disconnect" mode. It can be enabled
>> by setting mount option "no_disconnect=1", and results with defering
>> function disconnect to the moment of reopen ep0 file or filesystem
>> unmount. After closing all endpoint files, FunctionFS is set to state
>> FFS_DEACTIVATED.
>>
>> When ffs->state == FFS_DEACTIVATED:
>> - function is still bound and visible to host,
>> - setup requests are automatically stalled,
>> - transfers on other endpoints are refused,
>> - epfiles, except ep0, are deleted from the filesystem,
>> - opening ep0 causes the function to be closes, and then FunctionFS 
>>   is ready for descriptors and string write,
>> - unmounting of the FunctionFS instance causes the function to be closed.
>>
>> Signed-off-by: Robert Baldyga <r.baldyga@samsung.com>
> 
> Acked-by: Michal Nazarewicz <mina86@mina86.com>
> 
> Even though I have some concerns whether the mode should be enable via
> a mount option.  Perhaps it should come from the gadget or the daemon?
> I don't have strong feelings though, so I would be fine with the code as
> is.
> 

I think that setting this option by daemon isn't good idea. It's better
to have this feature transparent for ffs daemons, and allow user to
decide how the function should behave after daemon breakage/termination.
This feature makes sense only because we don't want to have entire
gadget work dependent on ffs daemon, which is userspace code and we have
no guarantee it works properly.

Thanks,
Robert Baldyga

>> ---
>>
>> Changelog:
>>
>> v3:
>> - change option name to more descriptive and less scary,
>> - fix cleaning up on unmount (call ffs_data_closed() in ffs_fs_kill_sb(),
>>   and ffs_data_clear() in ffs_data_closed() if ffs->opened is negative).
>>
>> v2: https://lkml.org/lkml/2014/10/7/109
>> - delete epfiles, excepting ep0, when FFS is in "zombie" mode,
>> - add description of FFS_ZOMBIE state,
>> - minor cleanups.
>>
>> v1: https://lkml.org/lkml/2014/10/6/128
>>
>>  drivers/usb/gadget/function/f_fs.c | 42 +++++++++++++++++++++++++++++++-------
>>  drivers/usb/gadget/function/u_fs.h | 22 ++++++++++++++++++++
>>  2 files changed, 57 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
>> index 12dbdaf..f326267 100644
>> --- a/drivers/usb/gadget/function/f_fs.c
>> +++ b/drivers/usb/gadget/function/f_fs.c
>> @@ -606,6 +606,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);
>> @@ -1152,6 +1154,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;
>>  };
>>  
>> @@ -1222,6 +1225,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;
>> @@ -1286,6 +1295,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;
>> @@ -1302,6 +1312,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)) {
>> @@ -1333,6 +1344,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);
>>  	}
>>  }
>> @@ -1389,7 +1401,9 @@ 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)
>> +		if (ffs->state == FFS_DEACTIVATED)
>> +			ffs_data_reset(ffs);
>>  }
>>  
>>  static void ffs_data_put(struct ffs_data *ffs)
>> @@ -1411,9 +1425,22 @@ static void ffs_data_closed(struct ffs_data *ffs)
>>  	ENTER();
>>  
>>  	if (atomic_dec_and_test(&ffs->opened)) {
>> -		ffs->state = FFS_CLOSING;
>> -		ffs_data_reset(ffs);
>> +		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_data_clear(ffs);
>>  
>>  	ffs_data_put(ffs);
>>  }
>> @@ -1588,7 +1615,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;
>> @@ -1601,10 +1627,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);
>>  }
>> diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
>> index cd128e3..7bc0ca2 100644
>> --- a/drivers/usb/gadget/function/u_fs.h
>> +++ b/drivers/usb/gadget/function/u_fs.h
>> @@ -93,6 +93,26 @@ enum ffs_state {
>>  	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
>>  	 * unrecoverable error is situation when after reading strings
>> @@ -251,6 +271,8 @@ struct ffs_data {
>>  		kgid_t				gid;
>>  	}				file_perms;
>>  
>> +	bool no_disconnect;
>> +
>>  	/*
>>  	 * The endpoint files, filled by ffs_epfiles_create(),
>>  	 * destroyed by ffs_epfiles_destroy().
>> -- 
>> 1.9.1
>>
> 


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-10-10  8:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-09 11:47 [PATCH v3] usb: gadget: f_fs: add "no_disconnect" mode Robert Baldyga
2014-10-09 13:57 ` Michal Nazarewicz
2014-10-10  8:10   ` Robert Baldyga

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).