From b7d5426deba1cf2e3733a0f07fa3393711a64aa2 Mon Sep 17 00:00:00 2001 From: William Wu Date: Mon, 10 Jun 2019 19:12:55 +0800 Subject: [PATCH] usb: gadget: uvc: support streaming bulk transfer This patch adds bulk endpoint for uvc to be used as video streaming transfer. By default, the uvc gadget still uses isoc endpoint for video streaming. The important difference between the bulk and isoc method is that, alt-settings in a video streaming interface are supported only for isoc 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. How to use: 1. Enable the bulk transfer: echo 1 > /config/usb-gadget/g1/functions/uvc.name/streaming_bulk 2. Disable the bulk transfer: echo 0 > /config/usb-gadget/g1/functions/uvc.name/streaming_bulk 3. Testing the uvc bulk function transfer: device: run the uvc-gadget program # uvc-gadget -b -u /dev/video -v /dev/video where uvc-gadget is this program: http://git.ideasonboard.org/uvc-gadget.git with these patches: http://www.spinics.net/lists/linux-usb/msg99220.html Change-Id: Ifedfe3f5c4354dd2bdf07382290107e9bcc89f59 Signed-off-by: William Wu --- drivers/usb/gadget/function/f_uvc.c | 348 +++++++++++++++++---- drivers/usb/gadget/function/u_uvc.h | 1 + drivers/usb/gadget/function/uvc_configfs.c | 3 + drivers/usb/gadget/function/uvc_v4l2.c | 18 +- drivers/usb/gadget/function/uvc_video.c | 11 +- 5 files changed, 305 insertions(+), 76 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 78312f0e3201..843e78c9e374 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -124,6 +124,18 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 = { .iInterface = 0, }; +static struct usb_interface_descriptor uvc_bulk_streaming_intf_alt0 = { + .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 = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, @@ -147,6 +159,16 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { */ }; +static struct usb_endpoint_descriptor uvc_fs_bulk_streaming_ep = { + .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. + */ +}; + static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -158,6 +180,16 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { */ }; +static struct usb_endpoint_descriptor uvc_hs_bulk_streaming_ep = { + .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. + */ +}; + static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -170,6 +202,17 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { */ }; +static struct usb_endpoint_descriptor uvc_ss_bulk_streaming_ep = { + .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. + */ +}; + static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, @@ -178,18 +221,36 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { */ }; +static struct usb_ss_ep_comp_descriptor uvc_ss_bulk_streaming_comp = { + .bLength = sizeof(uvc_ss_bulk_streaming_comp), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be + * initialized from module parameters. + */ +}; + 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, @@ -197,6 +258,11 @@ 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, +}; void uvc_set_trace_param(unsigned int trace) { uvc_gadget_trace_param = trace; @@ -271,15 +337,27 @@ static int uvc_function_get_alt(struct usb_function *f, unsigned interface) { struct uvc_device *uvc = to_uvc(f); + struct f_uvc_opts *opts; INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface); + opts = fi_to_f_uvc_opts(f->fi); + if (interface == uvc->control_intf) return 0; else if (interface != uvc->streaming_intf) return -EINVAL; - else + else if (!opts->streaming_bulk) return uvc->video.ep->enabled ? 1 : 0; + else + /* + * 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. + */ + return 0; } static int @@ -289,10 +367,13 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) struct usb_composite_dev *cdev = f->config->cdev; struct v4l2_event v4l2_event; struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; + struct f_uvc_opts *opts; int ret; INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); + opts = fi_to_f_uvc_opts(f->fi); + if (interface == uvc->control_intf) { if (alt) return -EINVAL; @@ -321,49 +402,89 @@ 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 (!opts->streaming_bulk) { + switch (alt) { + case 0: + if (uvc->state != UVC_STATE_STREAMING) + return 0; - switch (alt) { - case 0: - if (uvc->state != UVC_STATE_STREAMING) + 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); + + uvc->state = UVC_STATE_CONNECTED; return 0; - if (uvc->video.ep) + case 1: + if (uvc->state != UVC_STATE_CONNECTED) + return 0; + + if (!uvc->video.ep) + return -EINVAL; + + INFO(cdev, "reset UVC\n"); 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); + 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 && + !uvc->video.ep->enabled) { + /* + * Enable the video streaming endpoint, + * but don't change the 'uvc->state'. + */ + ret = config_ep_by_speed(cdev->gadget, + &uvc->func, + uvc->video.ep); + if (ret) + return ret; + ret = usb_ep_enable(uvc->video.ep); + if (ret) + return ret; + } 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) + case UVC_STATE_STREAMING: + if (uvc->video.ep && + uvc->video.ep->enabled) { + ret = usb_ep_disable(uvc->video.ep); + if (ret) + return ret; + } + + memset(&v4l2_event, 0, sizeof(v4l2_event)); + v4l2_event.type = UVC_EVENT_STREAMOFF; + v4l2_event_queue(&uvc->vdev, &v4l2_event); + uvc->state = UVC_STATE_CONNECTED; + return 0; + + default: return -EINVAL; - - INFO(cdev, "reset UVC\n"); - usb_ep_disable(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); - - 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; - - default: - return -EINVAL; + } } } @@ -479,32 +600,45 @@ 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; + struct f_uvc_opts *opts; unsigned int control_size; unsigned int streaming_size; unsigned int n_desc; unsigned int bytes; void *mem; + opts = fi_to_f_uvc_opts(uvc->func.fi); + switch (speed) { case USB_SPEED_SUPER: uvc_control_desc = uvc->desc.ss_control; uvc_streaming_cls = uvc->desc.ss_streaming; - uvc_streaming_std = uvc_ss_streaming; + if (!opts->streaming_bulk) + uvc_streaming_std = uvc_ss_streaming; + else + uvc_streaming_std = uvc_ss_bulk_streaming; break; case USB_SPEED_HIGH: uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.hs_streaming; - uvc_streaming_std = uvc_hs_streaming; + if (!opts->streaming_bulk) + uvc_streaming_std = uvc_hs_streaming; + else + uvc_streaming_std = uvc_hs_bulk_streaming; break; case USB_SPEED_FULL: default: uvc_control_desc = uvc->desc.fs_control; uvc_streaming_cls = uvc->desc.fs_streaming; - uvc_streaming_std = uvc_fs_streaming; + if (!opts->streaming_bulk) + uvc_streaming_std = uvc_fs_streaming; + else + uvc_streaming_std = uvc_fs_bulk_streaming; break; } @@ -524,12 +658,17 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) * uvc_{fs|hs}_streaming */ + if (!opts->streaming_bulk) + 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; @@ -579,7 +718,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, @@ -604,15 +743,24 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_uvc_opts *opts; int ret = -EINVAL; + u8 address; INFO(cdev, "uvc_function_bind\n"); opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ - opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); - opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); - opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + if (!opts->streaming_bulk) { + opts->streaming_interval = clamp(opts->streaming_interval, + 1U, 16U); + opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, + 1U, 3072U); + opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + } else { + opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, + 1U, 1024U); + opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); + } /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */ if (opts->streaming_maxburst && @@ -639,21 +787,37 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) max_packet_size = opts->streaming_maxpacket / 3; } - uvc_fs_streaming_ep.wMaxPacketSize = - cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); - uvc_fs_streaming_ep.bInterval = opts->streaming_interval; + if (!opts->streaming_bulk) { + uvc_fs_streaming_ep.wMaxPacketSize = + cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); + uvc_fs_streaming_ep.bInterval = opts->streaming_interval; - uvc_hs_streaming_ep.wMaxPacketSize = - cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); - uvc_hs_streaming_ep.bInterval = opts->streaming_interval; + uvc_hs_streaming_ep.wMaxPacketSize = + cpu_to_le16(max_packet_size | + ((max_packet_mult - 1) << 11)); + uvc_hs_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); - uvc_ss_streaming_ep.bInterval = opts->streaming_interval; - uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; - uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; - uvc_ss_streaming_comp.wBytesPerInterval = - cpu_to_le16(max_packet_size * max_packet_mult * - (opts->streaming_maxburst + 1)); + uvc_ss_streaming_ep.wMaxPacketSize = + cpu_to_le16(max_packet_size); + uvc_ss_streaming_ep.bInterval = opts->streaming_interval; + uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; + uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + cpu_to_le16(max_packet_size * max_packet_mult * + (opts->streaming_maxburst + 1)); + } else { + uvc_fs_bulk_streaming_ep.wMaxPacketSize = + cpu_to_le16(min(opts->streaming_maxpacket, 64U)); + + uvc_hs_bulk_streaming_ep.wMaxPacketSize = + cpu_to_le16(min(opts->streaming_maxpacket, 512U)); + + uvc_ss_bulk_streaming_ep.wMaxPacketSize = + cpu_to_le16(max_packet_size); + uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; + uvc_ss_streaming_comp.wBytesPerInterval = + cpu_to_le16(max_packet_size * opts->streaming_maxburst); + } /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); @@ -663,23 +827,57 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) } uvc->control_ep = ep; - 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 (!opts->streaming_bulk) + 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 (!opts->streaming_bulk) { + ep = usb_ep_autoconfig(cdev->gadget, + &uvc_hs_streaming_ep); + } else { + ep = usb_ep_autoconfig(cdev->gadget, + &uvc_hs_bulk_streaming_ep); + /* + * In ep_matches(), it will set wMaxPacketSize to 64 + * bytes if ep is Bulk and ep_comp is NULL for hs/fs + * bulk maxpacket. So we need to set hs bulk maxpacket + * 512 bytes again here. + */ + uvc_hs_bulk_streaming_ep.wMaxPacketSize = + cpu_to_le16(min(opts->streaming_maxpacket, + 512U)); + } + } else { + if (!opts->streaming_bulk) + 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"); goto error; } uvc->video.ep = ep; + address = uvc->video.ep->address; - 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 (!opts->streaming_bulk) { + uvc_fs_streaming_ep.bEndpointAddress = address; + uvc_hs_streaming_ep.bEndpointAddress = address; + uvc_ss_streaming_ep.bEndpointAddress = address; + } else { + uvc_fs_bulk_streaming_ep.bEndpointAddress = address; + uvc_hs_bulk_streaming_ep.bEndpointAddress = address; + uvc_ss_bulk_streaming_ep.bEndpointAddress = address; + } us = usb_gstrings_attach(cdev, uvc_function_strings, ARRAY_SIZE(uvc_en_us_strings)); @@ -690,8 +888,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; ret = us[UVC_STRING_STREAMING_IDX].id; - uvc_streaming_intf_alt0.iInterface = ret; - uvc_streaming_intf_alt1.iInterface = ret; + if (!opts->streaming_bulk) { + uvc_streaming_intf_alt0.iInterface = ret; + uvc_streaming_intf_alt1.iInterface = ret; + } else { + uvc_bulk_streaming_intf_alt0.iInterface = ret; + } /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) @@ -702,8 +904,14 @@ 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 (!opts->streaming_bulk) { + 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 */ @@ -752,6 +960,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (ret < 0) goto error; + if (opts->streaming_bulk) + uvc->video.max_payload_size = uvc->video.imagesize; /* Register a V4L2 device. */ ret = uvc_register_video(uvc); if (ret < 0) { diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 8129699747d4..fea15c9cbf73 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -22,6 +22,7 @@ DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(1, 1); struct f_uvc_opts { struct usb_function_instance func_inst; + bool streaming_bulk; unsigned int streaming_interval; unsigned int streaming_maxpacket; unsigned int streaming_maxburst; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 9e09f4a46d62..80665b229a8e 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2171,6 +2171,8 @@ UVC_ATTR(f_uvc_opts_, cname, cname) #define identity_conv(x) (x) +UVCG_OPTS_ATTR(streaming_bulk, streaming_bulk, identity_conv, + kstrtou8, u8, identity_conv, 1); UVCG_OPTS_ATTR(streaming_interval, streaming_interval, identity_conv, kstrtou8, u8, identity_conv, 16); UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, le16_to_cpu, @@ -2183,6 +2185,7 @@ UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, identity_conv, #undef UVCG_OPTS_ATTR static struct configfs_attribute *uvc_attrs[] = { + &f_uvc_opts_attr_streaming_bulk, &f_uvc_opts_attr_streaming_interval, &f_uvc_opts_attr_streaming_maxpacket, &f_uvc_opts_attr_streaming_maxburst, diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 8c992724c03f..097ee1adee8d 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -203,11 +203,21 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 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 (!usb_endpoint_xfer_bulk(video->ep->desc)) { + /* + * 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/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index d3567b90343a..e65a05f94833 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -237,9 +237,14 @@ 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); + if (!usb_endpoint_xfer_bulk(video->ep->desc)) { + req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + } 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);