Bhupesh Sharma
2013-04-11 09:34:04 UTC
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 <bhupesh.sharma-***@public.gmane.org>
---
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;
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 <bhupesh.sharma-***@public.gmane.org>
---
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 majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html