usb: gadget: add isochronous support to gadget zero
authorPaul Zimmerman <Paul.Zimmerman@synopsys.com>
Mon, 16 Apr 2012 21:19:06 +0000 (14:19 -0700)
committerFelipe Balbi <balbi@ti.com>
Fri, 4 May 2012 12:53:08 +0000 (15:53 +0300)
Add two isochronous endpoints to the gadget zero source/sink
function. They are enabled by selecting alternate interface 1, so
by default they are not enabled. Module parameters for setting all
the isoc endpoint characteristics are also provided.

Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
Signed-off-by: Paul Zimmerman <paulz@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/f_loopback.c
drivers/usb/gadget/f_sourcesink.c
drivers/usb/gadget/g_zero.h
drivers/usb/gadget/zero.c

index 2c0cd824c667488bb1c0e5b9fb5375f9086eae38..7275706caeb0534301354976c177e2123de908a9 100644 (file)
@@ -286,7 +286,7 @@ static void disable_loopback(struct f_loopback *loop)
        struct usb_composite_dev        *cdev;
 
        cdev = loop->function.config->cdev;
-       disable_endpoints(cdev, loop->in_ep, loop->out_ep);
+       disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
        VDBG(cdev, "%s disabled\n", loop->function.name);
 }
 
@@ -329,7 +329,7 @@ fail0:
         * than 'buflen' bytes each.
         */
        for (i = 0; i < qlen && result == 0; i++) {
-               req = alloc_ep_req(ep);
+               req = alloc_ep_req(ep, 0);
                if (req) {
                        req->complete = loopback_complete;
                        result = usb_ep_queue(ep, req, GFP_ATOMIC);
index 7aa7ac82c02c187cfd6af99eea6c4a937ad2e993..5c1b68b63c98da1e2182e74a8c92d3b3874fbdb1 100644 (file)
@@ -51,6 +51,9 @@ struct f_sourcesink {
 
        struct usb_ep           *in_ep;
        struct usb_ep           *out_ep;
+       struct usb_ep           *iso_in_ep;
+       struct usb_ep           *iso_out_ep;
+       int                     cur_alt;
 };
 
 static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
@@ -59,18 +62,45 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
 }
 
 static unsigned pattern;
-module_param(pattern, uint, 0);
-MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
+module_param(pattern, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
+
+static unsigned isoc_interval = 4;
+module_param(isoc_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_interval, "1 - 16");
+
+static unsigned isoc_maxpacket = 1024;
+module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+static unsigned isoc_mult;
+module_param(isoc_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
+
+static unsigned isoc_maxburst;
+module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_interface_descriptor source_sink_intf = {
-       .bLength =              sizeof source_sink_intf,
+static struct usb_interface_descriptor source_sink_intf_alt0 = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
        .bDescriptorType =      USB_DT_INTERFACE,
 
+       .bAlternateSetting =    0,
        .bNumEndpoints =        2,
        .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       /* .iInterface = DYNAMIC */
+       /* .iInterface          = DYNAMIC */
+};
+
+static struct usb_interface_descriptor source_sink_intf_alt1 = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        4,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       /* .iInterface          = DYNAMIC */
 };
 
 /* full speed support: */
@@ -91,10 +121,36 @@ static struct usb_endpoint_descriptor fs_sink_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
 };
 
+static struct usb_endpoint_descriptor fs_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1023),
+       .bInterval =            4,
+};
+
+static struct usb_endpoint_descriptor fs_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bEndpointAddress =     USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1023),
+       .bInterval =            4,
+};
+
 static struct usb_descriptor_header *fs_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf,
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
        (struct usb_descriptor_header *) &fs_sink_desc,
        (struct usb_descriptor_header *) &fs_source_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define FS_ALT_IFC_1_OFFSET    3
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       (struct usb_descriptor_header *) &fs_iso_sink_desc,
+       (struct usb_descriptor_header *) &fs_iso_source_desc,
        NULL,
 };
 
@@ -116,10 +172,34 @@ static struct usb_endpoint_descriptor hs_sink_desc = {
        .wMaxPacketSize =       cpu_to_le16(512),
 };
 
+static struct usb_endpoint_descriptor hs_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+static struct usb_endpoint_descriptor hs_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
 static struct usb_descriptor_header *hs_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf,
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
        (struct usb_descriptor_header *) &hs_source_desc,
        (struct usb_descriptor_header *) &hs_sink_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define HS_ALT_IFC_1_OFFSET    3
+       (struct usb_descriptor_header *) &hs_source_desc,
+       (struct usb_descriptor_header *) &hs_sink_desc,
+       (struct usb_descriptor_header *) &hs_iso_source_desc,
+       (struct usb_descriptor_header *) &hs_iso_sink_desc,
        NULL,
 };
 
@@ -136,6 +216,7 @@ static struct usb_endpoint_descriptor ss_source_desc = {
 struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
        .bLength =              USB_DT_SS_EP_COMP_SIZE,
        .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
        .bMaxBurst =            0,
        .bmAttributes =         0,
        .wBytesPerInterval =    0,
@@ -152,17 +233,64 @@ static struct usb_endpoint_descriptor ss_sink_desc = {
 struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
        .bLength =              USB_DT_SS_EP_COMP_SIZE,
        .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
        .bMaxBurst =            0,
        .bmAttributes =         0,
        .wBytesPerInterval =    0,
 };
 
+static struct usb_endpoint_descriptor ss_iso_source_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_iso_sink_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+
+       .bmAttributes =         USB_ENDPOINT_XFER_ISOC,
+       .wMaxPacketSize =       cpu_to_le16(1024),
+       .bInterval =            4,
+};
+
+struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
+       .bLength =              USB_DT_SS_EP_COMP_SIZE,
+       .bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+       .bMaxBurst =            0,
+       .bmAttributes =         0,
+       .wBytesPerInterval =    cpu_to_le16(1024),
+};
+
 static struct usb_descriptor_header *ss_source_sink_descs[] = {
-       (struct usb_descriptor_header *) &source_sink_intf,
+       (struct usb_descriptor_header *) &source_sink_intf_alt0,
        (struct usb_descriptor_header *) &ss_source_desc,
        (struct usb_descriptor_header *) &ss_source_comp_desc,
        (struct usb_descriptor_header *) &ss_sink_desc,
        (struct usb_descriptor_header *) &ss_sink_comp_desc,
+       (struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define SS_ALT_IFC_1_OFFSET    5
+       (struct usb_descriptor_header *) &ss_source_desc,
+       (struct usb_descriptor_header *) &ss_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_sink_desc,
+       (struct usb_descriptor_header *) &ss_sink_comp_desc,
+       (struct usb_descriptor_header *) &ss_iso_source_desc,
+       (struct usb_descriptor_header *) &ss_iso_source_comp_desc,
+       (struct usb_descriptor_header *) &ss_iso_sink_desc,
+       (struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
        NULL,
 };
 
@@ -196,9 +324,10 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
        id = usb_interface_id(c, f);
        if (id < 0)
                return id;
-       source_sink_intf.bInterfaceNumber = id;
+       source_sink_intf_alt0.bInterfaceNumber = id;
+       source_sink_intf_alt1.bInterfaceNumber = id;
 
-       /* allocate endpoints */
+       /* allocate bulk endpoints */
        ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
        if (!ss->in_ep) {
 autoconf_fail:
@@ -213,12 +342,74 @@ autoconf_fail:
                goto autoconf_fail;
        ss->out_ep->driver_data = cdev; /* claim */
 
+       /* sanity check the isoc module parameters */
+       if (isoc_interval < 1)
+               isoc_interval = 1;
+       if (isoc_interval > 16)
+               isoc_interval = 16;
+       if (isoc_mult > 2)
+               isoc_mult = 2;
+       if (isoc_maxburst > 15)
+               isoc_maxburst = 15;
+
+       /* fill in the FS isoc descriptors from the module parameters */
+       fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+       fs_iso_source_desc.bInterval = isoc_interval;
+       fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+       fs_iso_sink_desc.bInterval = isoc_interval;
+
+       /* allocate iso endpoints */
+       ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
+       if (!ss->iso_in_ep)
+               goto no_iso;
+       ss->iso_in_ep->driver_data = cdev;      /* claim */
+
+       ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
+       if (ss->iso_out_ep) {
+               ss->iso_out_ep->driver_data = cdev;     /* claim */
+       } else {
+               ss->iso_in_ep->driver_data = NULL;
+               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 (isoc_maxpacket > 1024)
+               isoc_maxpacket = 1024;
+
        /* support high speed hardware */
        if (gadget_is_dualspeed(c->cdev->gadget)) {
                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
+                * give parameters that their UDC doesn't support.
+                */
+               hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+               hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
+               hs_iso_source_desc.bInterval = isoc_interval;
+               hs_iso_source_desc.bEndpointAddress =
+                               fs_iso_source_desc.bEndpointAddress;
+
+               hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+               hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11;
+               hs_iso_sink_desc.bInterval = isoc_interval;
+               hs_iso_sink_desc.bEndpointAddress =
+                               fs_iso_sink_desc.bEndpointAddress;
+
                f->hs_descriptors = hs_source_sink_descs;
        }
 
@@ -228,13 +419,39 @@ autoconf_fail:
                                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
+                * give parameters that their UDC doesn't support.
+                */
+               ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+               ss_iso_source_desc.bInterval = isoc_interval;
+               ss_iso_source_comp_desc.bmAttributes = isoc_mult;
+               ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst;
+               ss_iso_source_comp_desc.wBytesPerInterval =
+                       isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
+               ss_iso_source_desc.bEndpointAddress =
+                               fs_iso_source_desc.bEndpointAddress;
+
+               ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+               ss_iso_sink_desc.bInterval = isoc_interval;
+               ss_iso_sink_comp_desc.bmAttributes = isoc_mult;
+               ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst;
+               ss_iso_sink_comp_desc.wBytesPerInterval =
+                       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;
        }
 
-       DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+       DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
            (gadget_is_superspeed(c->cdev->gadget) ? "super" :
             (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-                       f->name, ss->in_ep->name, ss->out_ep->name);
+                       f->name, ss->in_ep->name, ss->out_ep->name,
+                       ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
+                       ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
        return 0;
 }
 
@@ -251,6 +468,9 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
        u8                      *buf = req->buf;
        struct usb_composite_dev *cdev = ss->function.config->cdev;
 
+       if (pattern == 2)
+               return 0;
+
        for (i = 0; i < req->actual; i++, buf++) {
                switch (pattern) {
 
@@ -265,7 +485,7 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
                 * each usb transfer request should be.  Resync is done
                 * with set_interface or set_config.  (We *WANT* it to
                 * get quickly out of sync if controllers or their drivers
-                * stutter for any reason, including buffer duplcation...)
+                * stutter for any reason, including buffer duplication...)
                 */
                case 1:
                        if (*buf == (u8)(i % 63))
@@ -292,21 +512,30 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
                for  (i = 0; i < req->length; i++)
                        *buf++ = (u8) (i % 63);
                break;
+       case 2:
+               break;
        }
 }
 
 static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 {
-       struct f_sourcesink     *ss = ep->driver_data;
-       struct usb_composite_dev *cdev = ss->function.config->cdev;
-       int                     status = req->status;
+       struct usb_composite_dev        *cdev;
+       struct f_sourcesink             *ss = ep->driver_data;
+       int                             status = req->status;
+
+       /* driver_data will be null if ep has been disabled */
+       if (!ss)
+               return;
+
+       cdev = ss->function.config->cdev;
 
        switch (status) {
 
        case 0:                         /* normal completion? */
                if (ep == ss->out_ep) {
                        check_read_data(ss, req);
-                       memset(req->buf, 0x55, req->length);
+                       if (pattern != 2)
+                               memset(req->buf, 0x55, req->length);
                } else
                        reinit_write_data(ep, req);
                break;
@@ -344,32 +573,57 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
        }
 }
 
-static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
+static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
+               bool is_iso, int speed)
 {
        struct usb_ep           *ep;
        struct usb_request      *req;
-       int                     status;
+       int                     i, size, status;
+
+       for (i = 0; i < 8; i++) {
+               if (is_iso) {
+                       switch (speed) {
+                       case USB_SPEED_SUPER:
+                               size = isoc_maxpacket * (isoc_mult + 1) *
+                                               (isoc_maxburst + 1);
+                               break;
+                       case USB_SPEED_HIGH:
+                               size = isoc_maxpacket * (isoc_mult + 1);
+                               break;
+                       default:
+                               size = isoc_maxpacket > 1023 ?
+                                               1023 : isoc_maxpacket;
+                               break;
+                       }
+                       ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
+                       req = alloc_ep_req(ep, size);
+               } else {
+                       ep = is_in ? ss->in_ep : ss->out_ep;
+                       req = alloc_ep_req(ep, 0);
+               }
 
-       ep = is_in ? ss->in_ep : ss->out_ep;
-       req = alloc_ep_req(ep);
-       if (!req)
-               return -ENOMEM;
+               if (!req)
+                       return -ENOMEM;
 
-       req->complete = source_sink_complete;
-       if (is_in)
-               reinit_write_data(ep, req);
-       else
-               memset(req->buf, 0x55, req->length);
+               req->complete = source_sink_complete;
+               if (is_in)
+                       reinit_write_data(ep, req);
+               else if (pattern != 2)
+                       memset(req->buf, 0x55, req->length);
 
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (status) {
-               struct usb_composite_dev        *cdev;
+               status = usb_ep_queue(ep, req, GFP_ATOMIC);
+               if (status) {
+                       struct usb_composite_dev        *cdev;
 
-               cdev = ss->function.config->cdev;
-               ERROR(cdev, "start %s %s --> %d\n",
-                               is_in ? "IN" : "OUT",
-                               ep->name, status);
-               free_ep_req(ep, req);
+                       cdev = ss->function.config->cdev;
+                       ERROR(cdev, "start %s%s %s --> %d\n",
+                             is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+                             ep->name, status);
+                       free_ep_req(ep, req);
+               }
+
+               if (!is_iso)
+                       break;
        }
 
        return status;
@@ -380,17 +634,20 @@ static void disable_source_sink(struct f_sourcesink *ss)
        struct usb_composite_dev        *cdev;
 
        cdev = ss->function.config->cdev;
-       disable_endpoints(cdev, ss->in_ep, ss->out_ep);
+       disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
+                       ss->iso_out_ep);
        VDBG(cdev, "%s disabled\n", ss->function.name);
 }
 
 static int
-enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
+enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
+               int alt)
 {
        int                                     result = 0;
+       int                                     speed = cdev->gadget->speed;
        struct usb_ep                           *ep;
 
-       /* one endpoint writes (sources) zeroes IN (to the host) */
+       /* one bulk endpoint writes (sources) zeroes IN (to the host) */
        ep = ss->in_ep;
        result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
        if (result)
@@ -400,7 +657,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
                return result;
        ep->driver_data = ss;
 
-       result = source_sink_start_ep(ss, true);
+       result = source_sink_start_ep(ss, true, false, speed);
        if (result < 0) {
 fail:
                ep = ss->in_ep;
@@ -409,7 +666,7 @@ fail:
                return result;
        }
 
-       /* one endpoint reads (sinks) anything OUT (from the host) */
+       /* one bulk endpoint reads (sinks) anything OUT (from the host) */
        ep = ss->out_ep;
        result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
        if (result)
@@ -419,27 +676,82 @@ fail:
                goto fail;
        ep->driver_data = ss;
 
-       result = source_sink_start_ep(ss, false);
+       result = source_sink_start_ep(ss, false, false, speed);
        if (result < 0) {
+fail2:
+               ep = ss->out_ep;
                usb_ep_disable(ep);
                ep->driver_data = NULL;
                goto fail;
        }
 
-       DBG(cdev, "%s enabled\n", ss->function.name);
+       if (alt == 0)
+               goto out;
+
+       /* one iso endpoint writes (sources) zeroes IN (to the host) */
+       ep = ss->iso_in_ep;
+       if (ep) {
+               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+               if (result)
+                       goto fail2;
+               result = usb_ep_enable(ep);
+               if (result < 0)
+                       goto fail2;
+               ep->driver_data = ss;
+
+               result = source_sink_start_ep(ss, true, true, speed);
+               if (result < 0) {
+fail3:
+                       ep = ss->iso_in_ep;
+                       if (ep) {
+                               usb_ep_disable(ep);
+                               ep->driver_data = NULL;
+                       }
+                       goto fail2;
+               }
+       }
+
+       /* one iso endpoint reads (sinks) anything OUT (from the host) */
+       ep = ss->iso_out_ep;
+       if (ep) {
+               result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+               if (result)
+                       goto fail3;
+               result = usb_ep_enable(ep);
+               if (result < 0)
+                       goto fail3;
+               ep->driver_data = ss;
+
+               result = source_sink_start_ep(ss, false, true, speed);
+               if (result < 0) {
+                       usb_ep_disable(ep);
+                       ep->driver_data = NULL;
+                       goto fail3;
+               }
+       }
+out:
+       ss->cur_alt = alt;
+
+       DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
        return result;
 }
 
 static int sourcesink_set_alt(struct usb_function *f,
                unsigned intf, unsigned alt)
 {
-       struct f_sourcesink     *ss = func_to_ss(f);
-       struct usb_composite_dev *cdev = f->config->cdev;
+       struct f_sourcesink             *ss = func_to_ss(f);
+       struct usb_composite_dev        *cdev = f->config->cdev;
 
-       /* we know alt is zero */
        if (ss->in_ep->driver_data)
                disable_source_sink(ss);
-       return enable_source_sink(cdev, ss);
+       return enable_source_sink(cdev, ss, alt);
+}
+
+static int sourcesink_get_alt(struct usb_function *f, unsigned intf)
+{
+       struct f_sourcesink             *ss = func_to_ss(f);
+
+       return ss->cur_alt;
 }
 
 static void sourcesink_disable(struct usb_function *f)
@@ -465,6 +777,7 @@ static int __init sourcesink_bind_config(struct usb_configuration *c)
        ss->function.bind = sourcesink_bind;
        ss->function.unbind = sourcesink_unbind;
        ss->function.set_alt = sourcesink_set_alt;
+       ss->function.get_alt = sourcesink_get_alt;
        ss->function.disable = sourcesink_disable;
 
        status = usb_add_function(c, &ss->function);
@@ -536,7 +849,7 @@ unknown:
                req->length = value;
                value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
                if (value < 0)
-                       ERROR(c->cdev, "source/sinkc response, err %d\n",
+                       ERROR(c->cdev, "source/sink response, err %d\n",
                                        value);
        }
 
@@ -545,12 +858,12 @@ unknown:
 }
 
 static struct usb_configuration sourcesink_driver = {
-       .label          = "source/sink",
-       .strings        = sourcesink_strings,
-       .setup          = sourcesink_setup,
-       .bConfigurationValue = 3,
-       .bmAttributes   = USB_CONFIG_ATT_SELFPOWER,
-       /* .iConfiguration = DYNAMIC */
+       .label                  = "source/sink",
+       .strings                = sourcesink_strings,
+       .setup                  = sourcesink_setup,
+       .bConfigurationValue    = 3,
+       .bmAttributes           = USB_CONFIG_ATT_SELFPOWER,
+       /* .iConfiguration      = DYNAMIC */
 };
 
 /**
@@ -567,7 +880,8 @@ int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
                return id;
        strings_sourcesink[0].id = id;
 
-       source_sink_intf.iInterface = id;
+       source_sink_intf_alt0.iInterface = id;
+       source_sink_intf_alt1.iInterface = id;
        sourcesink_driver.iConfiguration = id;
 
        /* support autoresume for remote wakeup testing */
index e84b3c47ed3c3da1d9676b5a32055eac8caad6fd..71ca193358b85c23a039e12bdead188425c0490c 100644 (file)
@@ -13,10 +13,11 @@ extern unsigned buflen;
 extern const struct usb_descriptor_header *otg_desc[];
 
 /* common utilities */
-struct usb_request *alloc_ep_req(struct usb_ep *ep);
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len);
 void free_ep_req(struct usb_ep *ep, struct usb_request *req);
 void disable_endpoints(struct usb_composite_dev *cdev,
-               struct usb_ep *in, struct usb_ep *out);
+               struct usb_ep *in, struct usb_ep *out,
+               struct usb_ep *iso_in, struct usb_ep *iso_out);
 
 /* configuration-specific linkup */
 int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume);
index 31d34832907e44e48f91e7a937e2a18df556f3f9..12ad516ada77110a236600647feeaf3bbdbf954d 100644 (file)
@@ -72,7 +72,7 @@
 
 static const char longname[] = "Gadget Zero";
 
-unsigned buflen = 4096;
+unsigned buflen = 4096;                /* only used for bulk endpoints */
 module_param(buflen, uint, 0);
 
 /*
@@ -170,14 +170,17 @@ static struct usb_gadget_strings *dev_strings[] = {
 
 /*-------------------------------------------------------------------------*/
 
-struct usb_request *alloc_ep_req(struct usb_ep *ep)
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len)
 {
        struct usb_request      *req;
 
        req = usb_ep_alloc_request(ep, GFP_ATOMIC);
        if (req) {
-               req->length = buflen;
-               req->buf = kmalloc(buflen, GFP_ATOMIC);
+               if (len)
+                       req->length = len;
+               else
+                       req->length = buflen;
+               req->buf = kmalloc(req->length, GFP_ATOMIC);
                if (!req->buf) {
                        usb_ep_free_request(ep, req);
                        req = NULL;
@@ -206,10 +209,15 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
 }
 
 void disable_endpoints(struct usb_composite_dev *cdev,
-               struct usb_ep *in, struct usb_ep *out)
+               struct usb_ep *in, struct usb_ep *out,
+               struct usb_ep *iso_in, struct usb_ep *iso_out)
 {
        disable_ep(cdev, in);
        disable_ep(cdev, out);
+       if (iso_in)
+               disable_ep(cdev, iso_in);
+       if (iso_out)
+               disable_ep(cdev, iso_out);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -311,7 +319,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
                device_desc.bcdDevice = cpu_to_le16(0x9999);
        }
 
-
        INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
 
        snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",