Hello, I sent this patch on the linux-uvc ML but it seems effectively dead nowadays. 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