The descriptor are updated at bind-time. Provide a copy of each
descriptor so they two functions of the same kind without chaning the
same descriptor. At bind time we know the capabilit of the UDC and they
don't change so a copy of HS descriptors is not passed if the UDC does
not support HS speed. Same goes for SS. This safes a few bytes of
memory.

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
 drivers/usb/gadget/config.c       |   39 ++++++++++++++++++++++++++++++++++++-
 drivers/usb/gadget/f_loopback.c   |   10 +++++++---
 drivers/usb/gadget/f_sourcesink.c |   10 ++++++----
 include/linux/usb/gadget.h        |    7 +++++++
 4 files changed, 58 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index e3a9892..34e12fc 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -19,7 +19,7 @@
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
-
+#include <linux/usb/composite.h>
 
 /**
  * usb_descriptor_fillbuf - fill buffer with descriptors
@@ -158,3 +158,40 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
        return ret;
 }
 EXPORT_SYMBOL_GPL(usb_copy_descriptors);
+
+int usb_assign_descriptors(struct usb_function *f,
+               struct usb_descriptor_header **fs,
+               struct usb_descriptor_header **hs,
+               struct usb_descriptor_header **ss)
+{
+       struct usb_gadget *g = f->config->cdev->gadget;
+
+       if (fs) {
+               f->fs_descriptors = usb_copy_descriptors(fs);
+               if (!f->fs_descriptors)
+                       goto err;
+       }
+       if (hs && gadget_is_dualspeed(g)) {
+               f->hs_descriptors = usb_copy_descriptors(hs);
+               if (!f->hs_descriptors)
+                       goto err;
+       }
+       if (ss && gadget_is_superspeed(g)) {
+               f->ss_descriptors = usb_copy_descriptors(ss);
+               if (!f->ss_descriptors)
+                       goto err;
+       }
+       return 0;
+err:
+       usb_free_all_descriptors(f);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(usb_assign_descriptors);
+
+void usb_free_all_descriptors(struct usb_function *f)
+{
+       usb_free_descriptors(f->fs_descriptors);
+       usb_free_descriptors(f->hs_descriptors);
+       usb_free_descriptors(f->ss_descriptors);
+}
+EXPORT_SYMBOL_GPL(usb_free_all_descriptors);
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index aaf707e..bb39cb2 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -177,6 +177,7 @@ loopback_bind(struct usb_configuration *c, struct 
usb_function *f)
        struct usb_composite_dev *cdev = c->cdev;
        struct f_loopback       *loop = func_to_loop(f);
        int                     id;
+       int ret;
 
        /* allocate interface ID(s) */
        id = usb_interface_id(c, f);
@@ -204,13 +205,16 @@ autoconf_fail:
        hs_loop_source_desc.bEndpointAddress =
                fs_loop_source_desc.bEndpointAddress;
        hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-       f->hs_descriptors = hs_loopback_descs;
 
        /* support super speed hardware */
        ss_loop_source_desc.bEndpointAddress =
                fs_loop_source_desc.bEndpointAddress;
        ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
-       f->ss_descriptors = ss_loopback_descs;
+
+       ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
+                       ss_loopback_descs);
+       if (ret)
+               return ret;
 
        DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
            (gadget_is_superspeed(c->cdev->gadget) ? "super" :
@@ -222,6 +226,7 @@ autoconf_fail:
 static void
 loopback_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+       usb_free_all_descriptors(f);
        kfree(func_to_loop(f));
 }
 
@@ -373,7 +378,6 @@ static int __init loopback_bind_config(struct 
usb_configuration *c)
                return -ENOMEM;
 
        loop->function.name = "loopback";
-       loop->function.descriptors = fs_loopback_descs;
        loop->function.bind = loopback_bind;
        loop->function.unbind = loopback_unbind;
        loop->function.set_alt = loopback_set_alt;
diff --git a/drivers/usb/gadget/f_sourcesink.c 
b/drivers/usb/gadget/f_sourcesink.c
index 9d8bb0a..6ef3ea2 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -319,6 +319,7 @@ sourcesink_bind(struct usb_configuration *c, struct 
usb_function *f)
        struct usb_composite_dev *cdev = c->cdev;
        struct f_sourcesink     *ss = func_to_ss(f);
        int     id;
+       int ret;
 
        /* allocate interface ID(s) */
        id = usb_interface_id(c, f);
@@ -406,8 +407,6 @@ no_iso:
        hs_iso_sink_desc.bInterval = isoc_interval;
        hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
 
-       f->hs_descriptors = hs_source_sink_descs;
-
        /* support super speed hardware */
        ss_source_desc.bEndpointAddress =
                fs_source_desc.bEndpointAddress;
@@ -436,7 +435,10 @@ no_iso:
                isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
        ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
 
-       f->ss_descriptors = ss_source_sink_descs;
+       ret = usb_assign_descriptors(f, fs_source_sink_descs, 
hs_source_sink_descs,
+                       ss_source_sink_descs);
+       if (ret)
+               return ret;
 
        DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
            (gadget_is_superspeed(c->cdev->gadget) ? "super" :
@@ -450,6 +452,7 @@ no_iso:
 static void
 sourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
 {
+       usb_free_all_descriptors(f);
        kfree(func_to_ss(f));
 }
 
@@ -765,7 +768,6 @@ static int __init sourcesink_bind_config(struct 
usb_configuration *c)
                return -ENOMEM;
 
        ss->function.name = "source/sink";
-       ss->function.descriptors = fs_source_sink_descs;
        ss->function.bind = sourcesink_bind;
        ss->function.unbind = sourcesink_unbind;
        ss->function.set_alt = sourcesink_set_alt;
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 5b6e508..0af6569 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -939,6 +939,13 @@ static inline void usb_free_descriptors(struct 
usb_descriptor_header **v)
        kfree(v);
 }
 
+struct usb_function;
+int usb_assign_descriptors(struct usb_function *f,
+               struct usb_descriptor_header **fs,
+               struct usb_descriptor_header **hs,
+               struct usb_descriptor_header **ss);
+void usb_free_all_descriptors(struct usb_function *f);
+
 /*-------------------------------------------------------------------------*/
 
 /* utility to simplify map/unmap of usb_requests to/from DMA */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to