diff mbox

[media] uvcvideo: Add iFunction or iInterface to device names.

Message ID 20170406175825.90406-1-pbos@google.com (mailing list archive)
State New, archived
Headers show

Commit Message

Peter Boström April 6, 2017, 5:58 p.m. UTC
Permits distinguishing between two /dev/videoX entries from the same
physical UVC device (that naturally share the same iProduct name).

This change matches current Windows behavior by prioritizing iFunction
over iInterface, but unlike Windows it displays both iProduct and
iFunction/iInterface strings when both are available.

Signed-off-by: Peter Boström <pbos@google.com>
---
 drivers/media/usb/uvc/uvc_driver.c | 43 +++++++++++++++++++++++++++++++-------
 drivers/media/usb/uvc/uvcvideo.h   |  4 +++-
 2 files changed, 39 insertions(+), 8 deletions(-)

Comments

Peter Boström April 6, 2017, 6:28 p.m. UTC | #1
I'll put some more info/discussion points inline. Very happy for
feedback/input here, thanks!

On Thu, Apr 6, 2017 at 1:58 PM Peter Boström <pbos@google.com> wrote:
>
> Permits distinguishing between two /dev/videoX entries from the same
> physical UVC device (that naturally share the same iProduct name).

The device under test has interface associations (and has iFunction
present, but not iInterface present). This device is enumerated as
"Camera Name: Interface Function" after this patch, instead of two
/dev/videoX entries showing up as "Camera Name" with different
functions not user visible (apart from lsusb). My tested "Logitech
Webcam C930e" shows no additional string (has only iProduct out of
these). I haven't tested any other devices (none with iInterface
present), and this device is still under development, so any
experience or input from interpretating the USB standard is
appreciated here.

> This change matches current Windows behavior by prioritizing iFunction
> over iInterface, but unlike Windows it displays both iProduct and
> iFunction/iInterface strings when both are available.

Windows only displays one of them, but I thought removing iProduct
from the string was scary. Do we want to match Windows for
consistency/keeping names short here, or is displaying both (which I
personally thinks make more sense) a good strategy here? "Should"
iFunction be expected to include the product name? Otherwise I think
it's fair to display both since something generic like "main video
feed" doesn't tie back to a specific device.

-------------------------------------------------------------------------
String descriptors present              |    Device function name used
-------------------------------------------------------------------------
iProduct                                |    iProduct
iProduct + iFunction                    |    iFunction
iProduct + iFunction + iInterface       |    iFunction
iProduct + iInterface                   |    iInterface
-------------------------------------------------------------------------


> Signed-off-by: Peter Boström <pbos@google.com>
> ---
>  drivers/media/usb/uvc/uvc_driver.c | 43 +++++++++++++++++++++++++++++++-------
>  drivers/media/usb/uvc/uvcvideo.h   |  4 +++-
>  2 files changed, 39 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index 04bf35063c4c..66adf8a77e56 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -1998,6 +1998,8 @@ static int uvc_probe(struct usb_interface *intf,
>  {
>         struct usb_device *udev = interface_to_usbdev(intf);
>         struct uvc_device *dev;
> +       char additional_name_buf[UVC_DEVICE_NAME_SIZE];
> +       const char *additional_name = NULL;
>         int ret;
>
>         if (id->idVendor && id->idProduct)
> @@ -2025,13 +2027,40 @@ static int uvc_probe(struct usb_interface *intf,
>         dev->quirks = (uvc_quirks_param == -1)
>                     ? id->driver_info : uvc_quirks_param;
>
> -       if (udev->product != NULL)
> -               strlcpy(dev->name, udev->product, sizeof dev->name);
> -       else
> -               snprintf(dev->name, sizeof dev->name,
> -                       "UVC Camera (%04x:%04x)",
> -                       le16_to_cpu(udev->descriptor.idVendor),
> -                       le16_to_cpu(udev->descriptor.idProduct));
> +       /*
> +        * Add iFunction or iInterface to names when available as additional
> +        * distinguishers between interfaces. iFunction is prioritized over
> +        * iInterface which matches Windows behavior at the point of writing.
> +        */
> +       if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) {
> +               usb_string(udev, intf->intf_assoc->iFunction,
> +                          additional_name_buf, sizeof(additional_name_buf));
> +               additional_name = additional_name_buf;
> +       } else if (intf->cur_altsetting->desc.iInterface != 0) {
> +               usb_string(udev, intf->cur_altsetting->desc.iInterface,
> +                          additional_name_buf, sizeof(additional_name_buf));
> +               additional_name = additional_name_buf;
> +       }
> +
> +       if (additional_name) {
> +               if (udev->product) {
> +                       snprintf(dev->name, sizeof(dev->name), "%s: %s",
> +                                udev->product, additional_name);
> +               } else {
> +                       snprintf(dev->name, sizeof(dev->name),
> +                                "UVC Camera: %s (%04x:%04x)",
> +                                additional_name,
> +                                le16_to_cpu(udev->descriptor.idVendor),
> +                                le16_to_cpu(udev->descriptor.idProduct));
> +               }
> +       } else if (udev->product) {
> +               strlcpy(dev->name, udev->product, sizeof(dev->name));
> +       } else {
> +               snprintf(dev->name, sizeof(dev->name),
> +                        "UVC Camera (%04x:%04x)",
> +                        le16_to_cpu(udev->descriptor.idVendor),
> +                        le16_to_cpu(udev->descriptor.idProduct));
> +       }
>
>         /* Parse the Video Class control descriptor. */
>         if (uvc_parse_control(dev) < 0) {
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 4205e7a423f0..0cbedaee6e19 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -541,13 +541,15 @@ struct uvc_streaming {
>         } clock;
>  };
>
> +#define UVC_DEVICE_NAME_SIZE   64

Note that this expands the device name, and is used because I believe
iProduct + iFunction strings can reasonably extend the previous 32
character max.

> +
>  struct uvc_device {
>         struct usb_device *udev;
>         struct usb_interface *intf;
>         unsigned long warnings;
>         __u32 quirks;
>         int intfnum;
> -       char name[32];
> +       char name[UVC_DEVICE_NAME_SIZE];
>
>         struct mutex lock;              /* Protects users */
>         unsigned int users;
> --
> 2.12.2.715.g7642488e1d-goog
>

Best,
- Peter
diff mbox

Patch

diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 04bf35063c4c..66adf8a77e56 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1998,6 +1998,8 @@  static int uvc_probe(struct usb_interface *intf,
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct uvc_device *dev;
+	char additional_name_buf[UVC_DEVICE_NAME_SIZE];
+	const char *additional_name = NULL;
 	int ret;
 
 	if (id->idVendor && id->idProduct)
@@ -2025,13 +2027,40 @@  static int uvc_probe(struct usb_interface *intf,
 	dev->quirks = (uvc_quirks_param == -1)
 		    ? id->driver_info : uvc_quirks_param;
 
-	if (udev->product != NULL)
-		strlcpy(dev->name, udev->product, sizeof dev->name);
-	else
-		snprintf(dev->name, sizeof dev->name,
-			"UVC Camera (%04x:%04x)",
-			le16_to_cpu(udev->descriptor.idVendor),
-			le16_to_cpu(udev->descriptor.idProduct));
+	/*
+	 * Add iFunction or iInterface to names when available as additional
+	 * distinguishers between interfaces. iFunction is prioritized over
+	 * iInterface which matches Windows behavior at the point of writing.
+	 */
+	if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) {
+		usb_string(udev, intf->intf_assoc->iFunction,
+			   additional_name_buf, sizeof(additional_name_buf));
+		additional_name = additional_name_buf;
+	} else if (intf->cur_altsetting->desc.iInterface != 0) {
+		usb_string(udev, intf->cur_altsetting->desc.iInterface,
+			   additional_name_buf, sizeof(additional_name_buf));
+		additional_name = additional_name_buf;
+	}
+
+	if (additional_name) {
+		if (udev->product) {
+			snprintf(dev->name, sizeof(dev->name), "%s: %s",
+				 udev->product, additional_name);
+		} else {
+			snprintf(dev->name, sizeof(dev->name),
+				 "UVC Camera: %s (%04x:%04x)",
+				 additional_name,
+				 le16_to_cpu(udev->descriptor.idVendor),
+				 le16_to_cpu(udev->descriptor.idProduct));
+		}
+	} else if (udev->product) {
+		strlcpy(dev->name, udev->product, sizeof(dev->name));
+	} else {
+		snprintf(dev->name, sizeof(dev->name),
+			 "UVC Camera (%04x:%04x)",
+			 le16_to_cpu(udev->descriptor.idVendor),
+			 le16_to_cpu(udev->descriptor.idProduct));
+	}
 
 	/* Parse the Video Class control descriptor. */
 	if (uvc_parse_control(dev) < 0) {
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 4205e7a423f0..0cbedaee6e19 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -541,13 +541,15 @@  struct uvc_streaming {
 	} clock;
 };
 
+#define UVC_DEVICE_NAME_SIZE	64
+
 struct uvc_device {
 	struct usb_device *udev;
 	struct usb_interface *intf;
 	unsigned long warnings;
 	__u32 quirks;
 	int intfnum;
-	char name[32];
+	char name[UVC_DEVICE_NAME_SIZE];
 
 	struct mutex lock;		/* Protects users */
 	unsigned int users;