usb: gadget: uvc: Disable interrupt endpoint by default
authorDaniel Scally <dan.scally@ideasonboard.com>
Mon, 30 Jan 2023 10:50:45 +0000 (10:50 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 2 Feb 2023 10:16:17 +0000 (11:16 +0100)
The f_uvc code includes an interrupt endpoint against the VideoControl
interface. According to section 2.4.2 of the UVC specification however
this endpoint is optional in at least some cases:

"This endpoint is optional, but may be mandatory under certain
conditions"

The conditions enumerated are whether...

1. The device supports hardware triggers
2. The device implements any AutoUpdate controls
3. The device implements any Asynchronous controls

As all of those things are implementation dependent, this endpoint
might be unnecessary for some users. Further to that it is unusable
in the current implementation as there is no mechanism within the
UVC gadget driver that allows data to be sent over that endpoint.
Disable the interrupt endpoint by default, but check whether the
user has asked for it to be enabled in configfs and continue to
generate it if so.

Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com>
Link: https://lore.kernel.org/r/20230130105045.120886-4-dan.scally@ideasonboard.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/uvc.h

index a673001f5271a0de06a6ac21d168baf39b22f5b8..5250805153c70446d6d22c8ea1b7f6f0e4ad9780 100644 (file)
@@ -76,7 +76,7 @@ static struct usb_interface_descriptor uvc_control_intf = {
        .bDescriptorType        = USB_DT_INTERFACE,
        .bInterfaceNumber       = UVC_INTF_VIDEO_CONTROL,
        .bAlternateSetting      = 0,
-       .bNumEndpoints          = 1,
+       .bNumEndpoints          = 0,
        .bInterfaceClass        = USB_CLASS_VIDEO,
        .bInterfaceSubClass     = UVC_SC_VIDEOCONTROL,
        .bInterfaceProtocol     = 0x00,
@@ -300,14 +300,17 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
                if (alt)
                        return -EINVAL;
 
-               uvcg_info(f, "reset UVC interrupt endpoint\n");
-               usb_ep_disable(uvc->interrupt_ep);
+               if (uvc->enable_interrupt_ep) {
+                       uvcg_info(f, "reset UVC interrupt endpoint\n");
+                       usb_ep_disable(uvc->interrupt_ep);
 
-               if (!uvc->interrupt_ep->desc)
-                       if (config_ep_by_speed(cdev->gadget, f, uvc->interrupt_ep))
-                               return -EINVAL;
+                       if (!uvc->interrupt_ep->desc)
+                               if (config_ep_by_speed(cdev->gadget, f,
+                                                      uvc->interrupt_ep))
+                                       return -EINVAL;
 
-               usb_ep_enable(uvc->interrupt_ep);
+                       usb_ep_enable(uvc->interrupt_ep);
+               }
 
                if (uvc->state == UVC_STATE_DISCONNECTED) {
                        memset(&v4l2_event, 0, sizeof(v4l2_event));
@@ -385,7 +388,8 @@ uvc_function_disable(struct usb_function *f)
        uvc->state = UVC_STATE_DISCONNECTED;
 
        usb_ep_disable(uvc->video.ep);
-       usb_ep_disable(uvc->interrupt_ep);
+       if (uvc->enable_interrupt_ep)
+               usb_ep_disable(uvc->interrupt_ep);
 }
 
 /* --------------------------------------------------------------------------
@@ -533,14 +537,17 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
        control_size = 0;
        streaming_size = 0;
        bytes = uvc_iad.bLength + uvc_control_intf.bLength
-             + uvc_interrupt_ep.bLength + uvc_interrupt_cs_ep.bLength
              + uvc_streaming_intf_alt0.bLength;
 
-       if (speed == USB_SPEED_SUPER) {
-               bytes += uvc_ss_interrupt_comp.bLength;
-               n_desc = 6;
-       } else {
-               n_desc = 5;
+       n_desc = 3;
+       if (uvc->enable_interrupt_ep) {
+               bytes += uvc_interrupt_ep.bLength + uvc_interrupt_cs_ep.bLength;
+               n_desc += 2;
+
+               if (speed == USB_SPEED_SUPER) {
+                       bytes += uvc_ss_interrupt_comp.bLength;
+                       n_desc += 1;
+               }
        }
 
        for (src = (const struct usb_descriptor_header **)uvc_control_desc;
@@ -579,11 +586,14 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
        uvc_control_header->bInCollection = 1;
        uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
 
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_interrupt_ep);
-       if (speed == USB_SPEED_SUPER)
-               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_interrupt_comp);
+       if (uvc->enable_interrupt_ep) {
+               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_interrupt_ep);
+               if (speed == USB_SPEED_SUPER)
+                       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_interrupt_comp);
+
+               UVC_COPY_DESCRIPTOR(mem, dst, &uvc_interrupt_cs_ep);
+       }
 
-       UVC_COPY_DESCRIPTOR(mem, dst, &uvc_interrupt_cs_ep);
        UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
 
        uvc_streaming_header = mem;
@@ -666,12 +676,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
                            (opts->streaming_maxburst + 1));
 
        /* Allocate endpoints. */
-       ep = usb_ep_autoconfig(cdev->gadget, &uvc_interrupt_ep);
-       if (!ep) {
-               uvcg_info(f, "Unable to allocate control EP\n");
-               goto error;
+       if (opts->enable_interrupt_ep) {
+               ep = usb_ep_autoconfig(cdev->gadget, &uvc_interrupt_ep);
+               if (!ep) {
+                       uvcg_info(f, "Unable to allocate interrupt EP\n");
+                       goto error;
+               }
+               uvc->interrupt_ep = ep;
+               uvc_control_intf.bNumEndpoints = 1;
        }
-       uvc->interrupt_ep = ep;
+       uvc->enable_interrupt_ep = opts->enable_interrupt_ep;
 
        if (gadget_is_superspeed(c->cdev->gadget))
                ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
index 48b71e04c2b1701f3c939c0b04b5968443aec2d6..daf226610f493f07fafed9fbdca81d2a6d793eaf 100644 (file)
@@ -149,6 +149,7 @@ struct uvc_device {
        struct usb_ep *interrupt_ep;
        struct usb_request *control_req;
        void *control_buf;
+       bool enable_interrupt_ep;
 
        unsigned int streaming_intf;