From c15a7cecb83bfa6839bc2d8d4cd3b0f8a1b24e76 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 Signed-off-by: Frank Wang --- drivers/usb/gadget/function/f_uvc.c | 354 +++++++++++++++++---- drivers/usb/gadget/function/u_uvc.h | 1 + drivers/usb/gadget/function/uvc_configfs.c | 2 + drivers/usb/gadget/function/uvc_v4l2.c | 18 +- drivers/usb/gadget/function/uvc_video.c | 14 +- 5 files changed, 312 insertions(+), 77 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 3c26508c804b..8aae498ab496 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,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 */ @@ -260,15 +327,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; uvcg_info(f, "%s(%u)\n", __func__, 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 @@ -278,10 +357,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; uvcg_info(f, "%s(%u, %u)\n", __func__, interface, alt); + opts = fi_to_f_uvc_opts(f->fi); + if (interface == uvc->control_intf) { if (alt) return -EINVAL; @@ -310,49 +392,94 @@ 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 (!alt) { + INFO(cdev, "bulk streaming intf not support alt 0\n"); + return 0; + } + + 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; - - uvcg_info(f, "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; + } } } @@ -467,32 +594,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; } @@ -512,12 +652,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; @@ -567,7 +712,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, @@ -592,15 +737,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; uvcg_info(f, "%s()\n", __func__); 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 && @@ -627,21 +781,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); @@ -651,23 +821,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) { uvcg_info(f, "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)); @@ -678,8 +882,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) @@ -691,8 +899,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; opts->streaming_interface = ret; @@ -742,6 +956,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (ret < 0) goto v4l2_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 c6e432d02578..ca9a7db23599 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 00fb58e50a15..8462da1c621d 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -2424,6 +2424,7 @@ end: \ \ UVC_ATTR(f_uvc_opts_, cname, cname) +UVCG_OPTS_ATTR(streaming_bulk, streaming_bulk, 1); UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16); UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072); UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); @@ -2431,6 +2432,7 @@ UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15); #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 5d37f787f89d..cbf971824a7f 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -201,11 +201,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 633e23d58d86..76d1bba0c2a3 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -87,6 +87,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, video->fid ^= UVC_STREAM_FID; video->payload_size = 0; + req->zero = 1; } if (video->payload_size == video->max_payload_size || @@ -135,7 +136,7 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) ret); /* Isochronous endpoints can't be halted. */ - if (usb_endpoint_xfer_bulk(video->ep->desc)) + if (video->ep->desc && usb_endpoint_xfer_bulk(video->ep->desc)) usb_ep_set_halt(video->ep); } @@ -203,9 +204,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);