This patch adds the support for Bulk endpoint to be used as video streaming
endpoint, on basis of a module parameter.

By default, the gadget still supports Isochronous endpoint for video streaming,
but if the module parameter 'bulk_streaming_ep' is set to 1, we can support
Bulk endpoint as well, which is useful for UDC's which don't support Isochronous
endpoints.

The important difference between the two implementations is that, alt-settings
in a video streaming interface are supported only for Isochronous endpoints as
there are different alt-settings for zero-bandwidth and full-bandwidth
use-cases, but the same is not true for Bulk endpoints, as they support only
a single alt-setting.

Signed-off-by: Bhupesh Sharma <[email protected]>
---
Note that to ease review and integration of this patch, I have rebased it
on Laurent's UVC gadget git tree available here (head uvc-gadget):
git://linuxtv.org/pinchartl/uvcvideo.git

This will allow the patch to be pulled into Felipe's repo in one go
after review and any subsequent rework (if required).

 drivers/usb/gadget/f_uvc.c     |  321 ++++++++++++++++++++++++++++++++--------
 drivers/usb/gadget/uvc.h       |    2 +
 drivers/usb/gadget/uvc_v4l2.c  |   17 ++-
 drivers/usb/gadget/uvc_video.c |   13 ++-
 4 files changed, 286 insertions(+), 67 deletions(-)

diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c
index 38dcedd..e5953eb 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/f_uvc.c
@@ -45,6 +45,11 @@ static unsigned int streaming_maxburst;
 module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
 
+static bool bulk_streaming_ep;
+module_param(bulk_streaming_ep, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(bulk_streaming_ep, "0 (Use ISOC video streaming ep) / "
+                                       "1 (Use BULK video streaming ep)");
+
 /* --------------------------------------------------------------------------
  * Function descriptors
  */
@@ -135,6 +140,19 @@ static struct usb_interface_descriptor 
uvc_streaming_intf_alt0 __initdata = {
        .iInterface             = 0,
 };
 
+static struct usb_interface_descriptor uvc_bulk_streaming_intf_alt0
+__initdata = {
+       .bLength                = USB_DT_INTERFACE_SIZE,
+       .bDescriptorType        = USB_DT_INTERFACE,
+       .bInterfaceNumber       = UVC_INTF_VIDEO_STREAMING,
+       .bAlternateSetting      = 0,
+       .bNumEndpoints          = 1,
+       .bInterfaceClass        = USB_CLASS_VIDEO,
+       .bInterfaceSubClass     = UVC_SC_VIDEOSTREAMING,
+       .bInterfaceProtocol     = 0x00,
+       .iInterface             = 0,
+};
+
 static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = {
        .bLength                = USB_DT_INTERFACE_SIZE,
        .bDescriptorType        = USB_DT_INTERFACE,
@@ -160,6 +178,18 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep 
__initdata = {
        .bInterval              = 0,
 };
 
+static struct usb_endpoint_descriptor uvc_fs_bulk_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+       .wMaxPacketSize         = 0,
+       .bInterval              = 0,
+};
+
 static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
        .bLength                = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType        = USB_DT_ENDPOINT,
@@ -173,6 +203,18 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep 
__initdata = {
        .bInterval              = 0,
 };
 
+static struct usb_endpoint_descriptor uvc_hs_bulk_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+       .wMaxPacketSize         = 0,
+       .bInterval              = 0,
+};
+
 static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
        .bLength                = USB_DT_ENDPOINT_SIZE,
        .bDescriptorType        = USB_DT_ENDPOINT,
@@ -187,6 +229,19 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep 
__initdata = {
        .bInterval              = 0,
 };
 
+static struct usb_endpoint_descriptor uvc_ss_bulk_streaming_ep __initdata = {
+       .bLength                = USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType        = USB_DT_ENDPOINT,
+
+       .bEndpointAddress       = USB_DIR_IN,
+       .bmAttributes           = USB_ENDPOINT_XFER_BULK,
+       /* The wMaxPacketSize and bInterval values will be initialized from
+        * module parameters.
+        */
+       .wMaxPacketSize         = 0,
+       .bInterval              = 0,
+};
+
 static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
        .bLength                = sizeof(uvc_ss_streaming_comp),
        .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
@@ -196,18 +251,38 @@ static struct usb_ss_ep_comp_descriptor 
uvc_ss_streaming_comp __initdata = {
        .wBytesPerInterval      = cpu_to_le16(1024),
 };
 
+static struct usb_ss_ep_comp_descriptor uvc_ss_bulk_streaming_comp
+__initdata = {
+       .bLength                = sizeof(uvc_ss_bulk_streaming_comp),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+       /* The following 3 values can be tweaked if necessary. */
+       .bMaxBurst              = 0,
+       .bmAttributes           = 0,
+       .wBytesPerInterval      = cpu_to_le16(1024),
+};
+
 static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
        (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
        (struct usb_descriptor_header *) &uvc_fs_streaming_ep,
        NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_fs_bulk_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_fs_bulk_streaming_ep,
+       NULL,
+};
+
 static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
        (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
        (struct usb_descriptor_header *) &uvc_hs_streaming_ep,
        NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_hs_bulk_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_hs_bulk_streaming_ep,
+       NULL,
+};
+
 static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
        (struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
        (struct usb_descriptor_header *) &uvc_ss_streaming_ep,
@@ -215,6 +290,12 @@ static const struct usb_descriptor_header * const 
uvc_ss_streaming[] = {
        NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_ss_bulk_streaming[] = {
+       (struct usb_descriptor_header *) &uvc_ss_bulk_streaming_ep,
+       (struct usb_descriptor_header *) &uvc_ss_bulk_streaming_comp,
+       NULL,
+};
+
 /* --------------------------------------------------------------------------
  * Control requests
  */
@@ -285,7 +366,17 @@ uvc_function_get_alt(struct usb_function *f, unsigned 
interface)
        else if (interface != uvc->streaming_intf)
                return -EINVAL;
        else
-               return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
+               /*
+                * Alt settings in an interface are supported only for
+                * ISOC endpoints as there are different alt-settings for
+                * zero-bandwidth and full-bandwidth cases, but the same
+                * is not true for BULK endpoints, as they have a single
+                * alt-setting.
+                */
+               if (!bulk_streaming_ep)
+                       return uvc->state == UVC_STATE_STREAMING ? 1 : 0;
+               else
+                       return 0;
 }
 
 static int
@@ -317,45 +408,91 @@ uvc_function_set_alt(struct usb_function *f, unsigned 
interface, unsigned alt)
        if (interface != uvc->streaming_intf)
                return -EINVAL;
 
-       /* TODO
-       if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
-               return alt ? -EINVAL : 0;
-       */
+       if (!bulk_streaming_ep) {
+               switch (alt) {
+               case 0:
+                       if (uvc->state != UVC_STATE_STREAMING)
+                               return 0;
+
+                       if (uvc->video.ep)
+                               usb_ep_disable(uvc->video.ep);
+
+                       memset(&v4l2_event, 0, sizeof(v4l2_event));
+                       v4l2_event.type = UVC_EVENT_STREAMOFF;
+                       v4l2_event_queue(uvc->vdev, &v4l2_event);
 
-       switch (alt) {
-       case 0:
-               if (uvc->state != UVC_STATE_STREAMING)
+                       uvc->state = UVC_STATE_CONNECTED;
                        return 0;
 
-               if (uvc->video.ep)
-                       usb_ep_disable(uvc->video.ep);
+               case 1:
+                       if (uvc->state != UVC_STATE_CONNECTED)
+                               return 0;
 
-               memset(&v4l2_event, 0, sizeof(v4l2_event));
-               v4l2_event.type = UVC_EVENT_STREAMOFF;
-               v4l2_event_queue(uvc->vdev, &v4l2_event);
+                       if (uvc->video.ep) {
+                               ret = config_ep_by_speed
+                                       (f->config->cdev->gadget,
+                                       &(uvc->func), uvc->video.ep);
+                               if (ret)
+                                       return ret;
+                               usb_ep_enable(uvc->video.ep);
+                       }
 
-               uvc->state = UVC_STATE_CONNECTED;
-               return 0;
+                       memset(&v4l2_event, 0, sizeof(v4l2_event));
+                       v4l2_event.type = UVC_EVENT_STREAMON;
+                       v4l2_event_queue(uvc->vdev, &v4l2_event);
+                       return USB_GADGET_DELAYED_STATUS;
 
-       case 1:
-               if (uvc->state != UVC_STATE_CONNECTED)
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               switch (uvc->state) {
+               case UVC_STATE_CONNECTED:
+                       if (!uvc->video.ep->driver_data) {
+                               /*
+                                * Enable the video streaming endpoint,
+                                * but don't change the 'uvc->state'.
+                                */
+                               if (uvc->video.ep) {
+                                       ret = config_ep_by_speed
+                                               (f->config->cdev->gadget,
+                                               &(uvc->func), uvc->video.ep);
+                                       if (ret)
+                                               return ret;
+                                       ret = usb_ep_enable(uvc->video.ep);
+                                       if (ret)
+                                               return ret;
+
+                                       uvc->video.ep->driver_data = uvc;
+                               }
+                       } else {
+                               memset(&v4l2_event, 0, sizeof(v4l2_event));
+                               v4l2_event.type = UVC_EVENT_STREAMON;
+                               v4l2_event_queue(uvc->vdev, &v4l2_event);
+
+                               uvc->state = UVC_STATE_STREAMING;
+                       }
                        return 0;
 
-               if (uvc->video.ep) {
-                       ret = config_ep_by_speed(f->config->cdev->gadget,
-                                       &(uvc->func), uvc->video.ep);
-                       if (ret)
-                               return ret;
-                       usb_ep_enable(uvc->video.ep);
-               }
+               case UVC_STATE_STREAMING:
+                       if (uvc->video.ep->driver_data) {
+                               if (uvc->video.ep) {
+                                       ret = usb_ep_disable(uvc->video.ep);
+                                       if (ret)
+                                               return ret;
+                               }
+                       }
 
-               memset(&v4l2_event, 0, sizeof(v4l2_event));
-               v4l2_event.type = UVC_EVENT_STREAMON;
-               v4l2_event_queue(uvc->vdev, &v4l2_event);
-               return USB_GADGET_DELAYED_STATUS;
+                       memset(&v4l2_event, 0, sizeof(v4l2_event));
+                       v4l2_event.type = UVC_EVENT_STREAMOFF;
+                       v4l2_event_queue(uvc->vdev, &v4l2_event);
 
-       default:
-               return -EINVAL;
+                       uvc->state = UVC_STATE_CONNECTED;
+                       return 0;
+
+               default:
+                       return -EINVAL;
+               }
        }
 }
 
@@ -450,6 +587,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum 
usb_device_speed speed)
        const struct uvc_descriptor_header * const *uvc_streaming_cls;
        const struct usb_descriptor_header * const *uvc_streaming_std;
        const struct usb_descriptor_header * const *src;
+       struct usb_interface_descriptor *streaming_intf_alt0;
        struct usb_descriptor_header **dst;
        struct usb_descriptor_header **hdr;
        unsigned int control_size;
@@ -492,12 +630,17 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum 
usb_device_speed speed)
         * uvc_{fs|hs}_streaming
         */
 
+       if (!bulk_streaming_ep)
+               streaming_intf_alt0 = &uvc_streaming_intf_alt0;
+       else
+               streaming_intf_alt0 = &uvc_bulk_streaming_intf_alt0;
+
        /* Count descriptors and compute their size. */
        control_size = 0;
        streaming_size = 0;
        bytes = uvc_iad.bLength + uvc_control_intf.bLength
              + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
-             + uvc_streaming_intf_alt0.bLength;
+             + streaming_intf_alt0->bLength;
 
        if (speed == USB_SPEED_SUPER) {
                bytes += uvc_ss_control_comp.bLength;
@@ -547,7 +690,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum 
usb_device_speed speed)
                UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
 
        UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
+       UVC_COPY_DESCRIPTOR(mem, dst, streaming_intf_alt0);
 
        uvc_streaming_header = mem;
        UVC_COPY_DESCRIPTORS(mem, dst,
@@ -594,11 +737,19 @@ uvc_function_bind(struct usb_configuration *c, struct 
usb_function *f)
 
        INFO(cdev, "uvc_function_bind\n");
 
+       /* Use Bulk endpoint for video streaming?. */
+       uvc->video.bulk_streaming_ep = bulk_streaming_ep;
+
        /* Sanity check the streaming endpoint module parameters.
         */
-       streaming_interval = clamp(streaming_interval, 1U, 16U);
-       streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
-       streaming_maxburst = min(streaming_maxburst, 15U);
+       if (!bulk_streaming_ep) {
+               streaming_interval = clamp(streaming_interval, 1U, 16U);
+               streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U);
+               streaming_maxburst = min(streaming_maxburst, 15U);
+       } else {
+               streaming_maxpacket = clamp(streaming_maxpacket, 1U, 1024U);
+               streaming_maxburst = min(streaming_maxburst, 15U);
+       }
 
        /* Fill in the FS/HS/SS Video Streaming specific descriptors from the
         * module parameters.
@@ -617,19 +768,34 @@ uvc_function_bind(struct usb_configuration *c, struct 
usb_function *f)
                max_packet_size = streaming_maxpacket / 3;
        }
 
-       uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U);
-       uvc_fs_streaming_ep.bInterval = streaming_interval;
+       if (!bulk_streaming_ep) {
+               uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket,
+                                                        1023U);
+               uvc_fs_streaming_ep.bInterval = streaming_interval;
+
+               uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
+               uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1)
+                                                       << 11);
+               uvc_hs_streaming_ep.bInterval = streaming_interval;
+
+               uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
+               uvc_ss_streaming_ep.bInterval = streaming_interval;
+               uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
+               uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+               uvc_ss_streaming_comp.wBytesPerInterval =
+                       max_packet_size * max_packet_mult * streaming_maxburst;
+       } else {
+               uvc_fs_bulk_streaming_ep.wMaxPacketSize =
+                                       min(streaming_maxpacket, 64U);
 
-       uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size;
-       uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11);
-       uvc_hs_streaming_ep.bInterval = streaming_interval;
+               uvc_hs_bulk_streaming_ep.wMaxPacketSize =
+                                       min(streaming_maxpacket, 512U);
 
-       uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size;
-       uvc_ss_streaming_ep.bInterval = streaming_interval;
-       uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
-       uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
-       uvc_ss_streaming_comp.wBytesPerInterval =
-               max_packet_size * max_packet_mult * streaming_maxburst;
+               uvc_ss_bulk_streaming_ep.wMaxPacketSize = max_packet_size;
+               uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst;
+               uvc_ss_streaming_comp.wBytesPerInterval =
+                       max_packet_size * streaming_maxburst;
+       }
 
        /* Allocate endpoints. */
        ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
@@ -640,13 +806,31 @@ uvc_function_bind(struct usb_configuration *c, struct 
usb_function *f)
        uvc->control_ep = ep;
        ep->driver_data = uvc;
 
-       if (gadget_is_superspeed(c->cdev->gadget))
-               ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
-                                         &uvc_ss_streaming_comp);
-       else if (gadget_is_dualspeed(cdev->gadget))
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
-       else
-               ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+       if (gadget_is_superspeed(c->cdev->gadget)) {
+               if (!bulk_streaming_ep)
+                       ep = usb_ep_autoconfig_ss(cdev->gadget,
+                                               &uvc_ss_streaming_ep,
+                                               &uvc_ss_streaming_comp);
+               else
+                       ep = usb_ep_autoconfig_ss(cdev->gadget,
+                                               &uvc_ss_bulk_streaming_ep,
+                                               &uvc_ss_bulk_streaming_comp);
+
+       } else if (gadget_is_dualspeed(cdev->gadget)) {
+               if (!bulk_streaming_ep)
+                       ep = usb_ep_autoconfig(cdev->gadget,
+                                       &uvc_hs_streaming_ep);
+               else
+                       ep = usb_ep_autoconfig(cdev->gadget,
+                                       &uvc_hs_bulk_streaming_ep);
+       } else {
+               if (!bulk_streaming_ep)
+                       ep = usb_ep_autoconfig(cdev->gadget,
+                                       &uvc_fs_streaming_ep);
+               else
+                       ep = usb_ep_autoconfig(cdev->gadget,
+                                       &uvc_fs_bulk_streaming_ep);
+       }
 
        if (!ep) {
                INFO(cdev, "Unable to allocate streaming EP\n");
@@ -655,9 +839,18 @@ uvc_function_bind(struct usb_configuration *c, struct 
usb_function *f)
        uvc->video.ep = ep;
        ep->driver_data = uvc;
 
-       uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-       uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-       uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+       if (!bulk_streaming_ep) {
+               uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+               uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+               uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+       } else {
+               uvc_fs_bulk_streaming_ep.bEndpointAddress =
+                                       uvc->video.ep->address;
+               uvc_hs_bulk_streaming_ep.bEndpointAddress =
+                                       uvc->video.ep->address;
+               uvc_ss_bulk_streaming_ep.bEndpointAddress =
+                                       uvc->video.ep->address;
+       }
 
        /* Allocate interface IDs. */
        if ((ret = usb_interface_id(c, f)) < 0)
@@ -668,8 +861,12 @@ uvc_function_bind(struct usb_configuration *c, struct 
usb_function *f)
 
        if ((ret = usb_interface_id(c, f)) < 0)
                goto error;
-       uvc_streaming_intf_alt0.bInterfaceNumber = ret;
-       uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+       if (!bulk_streaming_ep) {
+               uvc_streaming_intf_alt0.bInterfaceNumber = ret;
+               uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+       } else {
+               uvc_bulk_streaming_intf_alt0.bInterfaceNumber = ret;
+       }
        uvc->streaming_intf = ret;
 
        /* Copy descriptors */
@@ -806,8 +1003,12 @@ uvc_bind_config(struct usb_configuration *c,
                uvc_control_intf.iInterface =
                        uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id;
                ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id;
-               uvc_streaming_intf_alt0.iInterface = ret;
-               uvc_streaming_intf_alt1.iInterface = ret;
+               if (!bulk_streaming_ep) {
+                       uvc_streaming_intf_alt0.iInterface = ret;
+                       uvc_streaming_intf_alt1.iInterface = ret;
+               } else {
+                       uvc_bulk_streaming_intf_alt0.bInterfaceNumber = ret;
+               }
        }
 
        /* Register the function. */
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h
index 817e9e1..8756853 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/uvc.h
@@ -133,6 +133,8 @@ struct uvc_video
 
        struct uvc_video_queue queue;
        unsigned int fid;
+
+       bool bulk_streaming_ep;
 };
 
 enum uvc_state
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
index ad48e81..c9656dc 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/uvc_v4l2.c
@@ -250,11 +250,20 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, 
void *arg)
                        return ret;
 
                /*
-                * Complete the alternate setting selection setup phase now that
-                * userspace is ready to provide video frames.
+                * Alt settings in an interface are supported only for ISOC
+                * endpoints as there are different alt-settings for
+                * zero-bandwidth and full-bandwidth cases, but the same is not
+                * true for BULK endpoints, as they have a single alt-setting.
                 */
-               uvc_function_setup_continue(uvc);
-               uvc->state = UVC_STATE_STREAMING;
+               if (!video->bulk_streaming_ep) {
+                       /*
+                        * Complete the alternate setting selection setup
+                        * phase now that userspace is ready to provide video
+                        * frames.
+                        */
+                       uvc_function_setup_continue(uvc);
+                       uvc->state = UVC_STATE_STREAMING;
+               }
 
                return 0;
        }
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
index 71e896d..87ac526 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/uvc_video.c
@@ -238,9 +238,13 @@ uvc_video_alloc_requests(struct uvc_video *video)
 
        BUG_ON(video->req_size);
 
-       req_size = video->ep->maxpacket
-                * max_t(unsigned int, video->ep->maxburst, 1)
-                * (video->ep->mult + 1);
+       if (!video->bulk_streaming_ep)
+               req_size = video->ep->maxpacket
+                       * max_t(unsigned int, video->ep->maxburst, 1)
+                       * (video->ep->mult + 1);
+       else
+               req_size = video->ep->maxpacket
+                       * max_t(unsigned int, video->ep->maxburst, 1);
 
        for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
                video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
@@ -387,6 +391,9 @@ uvc_video_init(struct uvc_video *video)
        video->height = 240;
        video->imagesize = 320 * 240 * 2;
 
+       if (video->bulk_streaming_ep)
+               video->max_payload_size = video->imagesize;
+
        /* Initialize the video buffers queue. */
        uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
        return 0;
-- 
1.7.2.2

--
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