[PATCH 01/23] usb: gadget: f_sourcesink: make ISO altset user-selectable

From: Robert Baldyga
Date: Tue Nov 03 2015 - 07:54:47 EST


So far it was decided during the bind process whether is iso altsetting
included to f_sourcesink function or not. This decision was based on
availability of isochronous endpoints.

Since we can assemble gadget driver using composite framework and configfs
from many different functions, availability of given type of endpoint
can depend on selected components or even on their order in given
configuration.

This can result with non-obvious behavior - even small, seemingly unrelated
change in gadget configuration can decide if we have second altsetting with
iso endpoints in given sourcesink function instance or not.

Because of this it's way better to have additional parameter allowing user
to decide if he/she wants to have iso altsetting, and if iso altsetting is
included, and there are no iso endpoints available, function bind will fail
instead of silently allowing to have non-complete function bound.

Signed-off-by: Robert Baldyga <r.baldyga@xxxxxxxxxxx>
---
drivers/usb/gadget/function/f_sourcesink.c | 99 ++++++++++++++++++++----------
drivers/usb/gadget/function/g_zero.h | 3 +
drivers/usb/gadget/legacy/zero.c | 6 ++
3 files changed, 77 insertions(+), 31 deletions(-)

diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index d7646d3..1d6ec88 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -56,6 +56,7 @@ struct f_sourcesink {
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
+ unsigned isoc_enabled;
unsigned buflen;
};

@@ -347,17 +348,28 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)

/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
- if (!ss->in_ep) {
-autoconf_fail:
- ERROR(cdev, "%s: can't autoconfigure on %s\n",
- f->name, cdev->gadget->name);
- return -ENODEV;
- }
+ if (!ss->in_ep)
+ goto autoconf_fail;

ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
if (!ss->out_ep)
goto autoconf_fail;

+ /* support high speed hardware */
+ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+ hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+
+ /* support super speed hardware */
+ ss_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+ ss_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+
+ if (!ss->isoc_enabled) {
+ fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
+ hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
+ ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
+ goto no_iso;
+ }
+
/* sanity check the isoc module parameters */
if (ss->isoc_interval < 1)
ss->isoc_interval = 1;
@@ -379,30 +391,14 @@ autoconf_fail:
/* allocate iso endpoints */
ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
if (!ss->iso_in_ep)
- goto no_iso;
+ goto autoconf_fail;

ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
- if (!ss->iso_out_ep) {
- usb_ep_autoconfig_release(ss->iso_in_ep);
- ss->iso_in_ep = NULL;
-no_iso:
- /*
- * We still want to work even if the UDC doesn't have isoc
- * endpoints, so null out the alt interface that contains
- * them and continue.
- */
- fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
- hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
- ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
- }
+ if (!ss->iso_out_ep)
+ goto autoconf_fail;

if (ss->isoc_maxpacket > 1024)
ss->isoc_maxpacket = 1024;
-
- /* support high speed hardware */
- hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
- hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
-
/*
* Fill in the HS isoc descriptors from the module parameters.
* We assume that the user knows what they are doing and won't
@@ -419,12 +415,6 @@ no_iso:
hs_iso_sink_desc.bInterval = ss->isoc_interval;
hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;

- /* support super speed hardware */
- ss_source_desc.bEndpointAddress =
- fs_source_desc.bEndpointAddress;
- ss_sink_desc.bEndpointAddress =
- fs_sink_desc.bEndpointAddress;
-
/*
* Fill in the SS isoc descriptors from the module parameters.
* We assume that the user knows what they are doing and won't
@@ -447,6 +437,7 @@ no_iso:
(ss->isoc_mult + 1) * (ss->isoc_maxburst + 1);
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;

+no_iso:
ret = usb_assign_descriptors(f, fs_source_sink_descs,
hs_source_sink_descs, ss_source_sink_descs);
if (ret)
@@ -459,6 +450,11 @@ no_iso:
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
return 0;
+
+autoconf_fail:
+ ERROR(cdev, "%s: can't autoconfigure on %s\n",
+ f->name, cdev->gadget->name);
+ return -ENODEV;
}

static void
@@ -868,6 +864,7 @@ static struct usb_function *source_sink_alloc_func(
ss->isoc_maxpacket = ss_opts->isoc_maxpacket;
ss->isoc_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst;
+ ss->isoc_enabled = ss_opts->isoc_enabled;
ss->buflen = ss_opts->bulk_buflen;

ss->function.name = "source/sink";
@@ -1125,6 +1122,45 @@ static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst =
f_ss_opts_isoc_maxburst_show,
f_ss_opts_isoc_maxburst_store);

+static ssize_t f_ss_opts_isoc_enabled_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%u\n", opts->isoc_enabled);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_enabled_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ bool enabled;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = strtobool(page, &enabled);
+ if (ret)
+ goto end;
+
+ opts->isoc_enabled = enabled;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_enabled =
+ __CONFIGFS_ATTR(isoc_enabled, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_enabled_show,
+ f_ss_opts_isoc_enabled_store);
+
static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
{
int result;
@@ -1170,6 +1206,7 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_isoc_maxpacket.attr,
&f_ss_opts_isoc_mult.attr,
&f_ss_opts_isoc_maxburst.attr,
+ &f_ss_opts_isoc_enabled.attr,
&f_ss_opts_bulk_buflen.attr,
NULL,
};
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f1809..8a99071 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -10,6 +10,7 @@
#define GZERO_QLEN 32
#define GZERO_ISOC_INTERVAL 4
#define GZERO_ISOC_MAXPACKET 1024
+#define GZERO_ISOC_ENABLED 1

struct usb_zero_options {
unsigned pattern;
@@ -17,6 +18,7 @@ struct usb_zero_options {
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
+ unsigned isoc_enabled;
unsigned bulk_buflen;
unsigned qlen;
};
@@ -28,6 +30,7 @@ struct f_ss_opts {
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
+ unsigned isoc_enabled;
unsigned bulk_buflen;

/*
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 37a4100..3579310 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -66,6 +66,7 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
static struct usb_zero_options gzero_options = {
.isoc_interval = GZERO_ISOC_INTERVAL,
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
+ .isoc_enabled = GZERO_ISOC_ENABLED,
.bulk_buflen = GZERO_BULK_BUFLEN,
.qlen = GZERO_QLEN,
};
@@ -249,6 +250,10 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");

+module_param_named(isoc_enabled, gzero_options.isoc_enabled, uint,
+ S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_enabled, "0 - disabled, 1 - enabled");
+
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;

@@ -284,6 +289,7 @@ static int zero_bind(struct usb_composite_dev *cdev)
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
+ ss_opts->isoc_enabled = gzero_options.isoc_enabled;
ss_opts->bulk_buflen = gzero_options.bulk_buflen;

func_ss = usb_get_function(func_inst_ss);
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/