Re: [PATCH] uvcvideo: Hardcoded CTRL_QUERY GET_LEN for a lying device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 06/04/2017 03:41 PM, Alexandre Macabies wrote:
> Hello,

I forgot to Cc: the full list of maintainers for this patch. This follow-up
includes them. Sorry for the noise! My original email & patch is quoted below.

Best,
Alexandre

> This thread comes after two others[1][2] about a similar issue.
> 
> I own a USB video microscope[3] from Dino-Lite. Even if the constructor does
> not advertise it as being supported on Linux, it is mostly a "good citizen"
> camera: it registers as a standard USB video device and as such, it is properly
> recognized by uvcvideo.
> 
> This device is equipped with an integrated illuminator/lamp -- a set of LEDs.
> After some research (using a USB sniffer) I managed to identify the
> non-standard XU control used to switch this lamp on and off: one shall send
> either 80 01 f0 (off) or 80 01 f1 (on) to XU control unit 4 selector 3.
> 
> So at first I tried to send a raw ctrl_set using:
> 
>     $ uvcdynctrl -S 4:3 8001f0
>     [...]
>     query control size of : 1
>     [...]
>     ERROR: Unable to set the control value: Invalid argument. (Code: 3)
> 
> Indeed, the device reports this XU as being only 1 in length, but the payload
> has to be 3 bytes. So I assume there is a bug (or deliberate inaccuracy) in the
> GET_LEN reply from the device firmware. To overcome this issue, I compiled
> a patched version of uvcvideo in which uvc_query_ctrl[4] returns an hardcoded
> size of 3 for this specific device & UX control. I was finally able to switch
> the lamp on and off:
> 
>     $ uvcdynctrl -S 4:3 8001f0
>     [39252.854261] uvcvideo: Fixing USB a168:0870 UX control 4/3 len: 1 -> 3
>     [...]
>     query control size of : 3
>     [...]
>     set value of          : (LE)0x8001f0  (BE)0xf00180
>     [lamp goes off]
> 
> You can find the patch below. I abstracted it in the spirit of
> uvc_ctrl_fixup_xu_info[5] so we can add more entries to the table in the
> future. What do you think, would it be relevant to merge? AFAICT there is no
> API in uvcvideo or v4l for controlling this kind of illuminator/lamp features,
> so giving userland the ability to control the devices via XU by lying seems to
> be the only solution.
> 
> Best,
> 
> Alexandre
> 
> [1] "Dino-Lite uvc support", 2008, https://sourceforge.net/p/linux-uvc/mailman/message/29831153/
> [2] "switching light on device Dino-Lite Premier", 2013, https://sourceforge.net/p/linux-uvc/mailman/message/31219122/
> [3] https://www.dinolite.us/products/digital-microscopes/usb/basic/am4111t
> [4] http://elixir.free-electrons.com/linux/v4.11/source/drivers/media/usb/uvc/uvc_video.c#L72
> [5] http://elixir.free-electrons.com/linux/v4.11/source/drivers/media/usb/uvc/uvc_ctrl.c#L1593
> 
> Signed-off-by: Alexandre Macabies <web+oss@xxxxxxxxxxx>
> 
> ---
>  drivers/media/usb/uvc/uvc_video.c | 37 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
> 
> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> index 07a6c833ef7b..839dc02b4f33 100644
> --- a/drivers/media/usb/uvc/uvc_video.c
> +++ b/drivers/media/usb/uvc/uvc_video.c
> @@ -69,6 +69,40 @@ static const char *uvc_query_name(__u8 query)
>  	}
>  }
>  
> +static void uvc_fixup_query_ctrl_len(const struct uvc_device *dev, __u8 unit,
> +	__u8 cs, void *data)
> +{
> +	struct uvc_ctrl_fixup {
> +		struct usb_device_id id;
> +		u8 unit;
> +		u8 selector;
> +		u16 len;
> +	};
> +
> +	static const struct uvc_ctrl_fixup fixups[] = {
> +		// Dino-Lite Premier (AM4111T)
> +		{ { USB_DEVICE(0xa168, 0x0870) }, 4, 3, 3 },
> +	};
> +
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fixups); ++i) {
> +		if (!usb_match_one_id(dev->intf, &fixups[i].id))
> +			continue;
> +
> +		if (!(fixups[i].unit == unit && fixups[i].selector == cs))
> +			continue;
> +
> +		uvc_trace(UVC_TRACE_CONTROL,
> +			  "Fixing USB %04x:%04x %u/%u GET_LEN: %u -> %u",
> +			  fixups[i].id.idVendor, fixups[i].id.idProduct,
> +			  unit, cs,
> +			  le16_to_cpup((__le16 *)data), fixups[i].len);
> +		*((__le16 *)data) = cpu_to_le16(fixups[i].len);
> +		break;
> +	}
> +}
> +
>  int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
>  			__u8 intfnum, __u8 cs, void *data, __u16 size)
>  {
> @@ -83,6 +117,9 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
>  		return -EIO;
>  	}
>  
> +	if (query == UVC_GET_LEN && size == 2)
> +		uvc_fixup_query_ctrl_len(dev, unit, cs, data);
> +
>  	return 0;
>  }
>  
> 
> -- 
> 2.13.0
> 





[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux