public inbox for linux-media@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/4] media: uvcvideo: Map known XU controls
@ 2026-03-16 13:34 Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 1/4] media: uvcvideo: Import standard controls from uvcdynctrl Ricardo Ribalda
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-16 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman
  Cc: linux-media, linux-kernel, linux-usb, Ricardo Ribalda,
	Manav Gautama, Martin Rubli

The UVC driver uses a custom ioctl `UVCIOC_CTRL_MAP` to map XU controls
into v4l2 controls. The most well know user of this feature is the
uvcdynctrl app.

This app has a set of XML files which contains the list of mappings.
Some of these mappings are standard and other ones are custom.

This series move the standard mappings to the kernel driver, so
userspace do not need to depend on external apps to use them.

While we are at it we realized that some of the mappings can be harmful
for the privacy of the user. This series introduce a mechanism to block
those mappings.

While we are at it, we complete the deprecation of the nodrop parameter.
Ideally, this patch should belong in a different series, but then we
will have conflicts... and who wants to works twice?

I have tried this series with a Logitech Webcam Pro 9000, that has been
donated by Hans de Goede (Thanks Hans!!!).

Without this patch and uvcdynctrl the device has 14 controls. (Ctrls A)

With this patch the device has 15 controls (Ctrls B):
Ctrls A
+
control 0x009a090a `Focus, Absolute' min 0 max 255 step 0 default 0 current 0

With uvcdynctrl and this patch the device has 17 controls (Ctrls C):
Ctrls B
+
control 0x0a046d71 `Disable video processing' min 0 max 1 step 1 default 0 current 0
control 0x0a046d72 `Raw bits per pixel' min 0 max 1 step 1 default 0 current 0

With uvcdynctrl and without this patch the device has 19 controls:
Ctrls C
+
control 0x0a046d05 `LED1 Mode' min 0 max 3 step 1 default 3 current 3
  0: Off
  1: On
  2: Blinking
  3: Auto (*)
control 0x0a046d06 `LED1 Frequency' min 0 max 255 step 1 default 0 current 0

BTW, Driver tested with virtme-ng. First time that I use it for uvc
development, and it works like a charm :).
virtme-run --kimg arch/x86/boot/bzImage --mods auto --show-command \
	--show-boot-console --verbose --qemu-opts -usb -device qemu-xhci \
	-device usb-host,hostbus=1,hostport=4

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
Changes in v3:
- s/param/module parameter/ (Thanks HdG)
- Rebase to lastest media-committers/next and remove merged patches.
- Link to v2: https://lore.kernel.org/r/20251119-uvcdynctrl-v2-0-0359ffb98c9e@chromium.org

Changes in v2:
- Document how to get GUIDs
- Show clear deprecation intentions to the user.
- Add new patch for the Kconfig approach (Thanks Mauro).
- Remove duplicated control in commit message, provide background and
  add documentation. (Thanks Laurent)
- Block also uvc_query_ctrl for privacy controls (Thanks Gergo Koteles)
- Link to v1: https://lore.kernel.org/r/20251117-uvcdynctrl-v1-0-aed70eadf3d8@chromium.org

---
Ricardo Ribalda (4):
      media: uvcvideo: Import standard controls from uvcdynctrl
      media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP
      media: uvcvideo: Introduce allow_privacy_override module parameter
      media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig

 .../userspace-api/media/drivers/uvcvideo.rst       |   2 +
 drivers/media/usb/uvc/Kconfig                      |  11 ++
 drivers/media/usb/uvc/uvc_ctrl.c                   | 212 +++++++++++++++++++++
 drivers/media/usb/uvc/uvc_v4l2.c                   |  11 ++
 drivers/media/usb/uvc/uvcvideo.h                   |   1 +
 include/linux/usb/uvc.h                            |  10 +
 6 files changed, 247 insertions(+)
---
base-commit: f6390408a846aacc2171c17d88b062e202d84e86
change-id: 20251117-uvcdynctrl-7b80f5bfbb41

Best regards,
-- 
Ricardo Ribalda <ribalda@chromium.org>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 1/4] media: uvcvideo: Import standard controls from uvcdynctrl
  2026-03-16 13:34 [PATCH v3 0/4] media: uvcvideo: Map known XU controls Ricardo Ribalda
@ 2026-03-16 13:34 ` Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 2/4] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP Ricardo Ribalda
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-16 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman
  Cc: linux-media, linux-kernel, linux-usb, Ricardo Ribalda,
	Manav Gautama, Martin Rubli

The uvcdynctrl tool from libwebcam:
https://sourceforge.net/projects/libwebcam/
maps proprietary controls into v4l2 controls using the UVCIOC_CTRL_MAP
ioctl.

The tool has not been updated for 10+ years now, and there is no reason
for the UVC driver to not do the mapping by itself.

This patch adds the mappings from the uvcdynctrl into the driver. Hopefully
this effort can help in deprecating the UVCIOC_CTRL_MAP ioctl.

Some background about UVCIOC_CTRL_MAP (thanks Laurent for the context):

```
this was envisioned as the base of a vibrant ecosystem where a large
number of vendors would submit XML files that describe their XU control
mappings, at a pace faster than could be supported by adding XU mappings
to the driver. This vision failed to materialize and the tool has not
been updated for 10+ years now. There is no reason to believe the
situation will change.
```

During the porting, the following mappings where NOT imported because
they were not using standard v4l2 IDs. It is recommended that userspace
moves to UVCIOC_CTRL_QUERY for non standard controls.

        {
                .id             = V4L2_CID_FLASH_MODE,
                .entity         = UVC_GUID_SIS_LED_HW_CONTROL,
                .selector       = 4,
                .size           = 4,
                .offset         = 0,
                .v4l2_type      = V4L2_CTRL_TYPE_MENU,
                .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
                .menu_mask      = 0x3,
                .menu_mapping   = { 0x20, 0x22 },
                .menu_names     = { "Off", "On" },

        },
        {
                .id             = V4L2_CID_FLASH_FREQUENCY,
                .entity         = UVC_GUID_SIS_LED_HW_CONTROL,
                .selector       = 4,
                .size           = 8,
                .offset         = 16,
                .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
                .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
        },
       {
               .id             = V4L2_CID_LED1_MODE,
               .entity         = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
               .selector       = 1,
               .size           = 8,
               .offset         = 0,
               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
               .menu_mask      = 0xF,
               .menu_mapping   = { 0, 1, 2, 3 },
               .menu_names     = { "Off", "On", "Blinking", "Auto" },

       },
       {
               .id             = V4L2_CID_LED1_FREQUENCY,
               .entity         = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
               .selector       = 1,
               .size           = 8,
               .offset         = 16,
               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
       },
       {
               .id             = V4L2_CID_DISABLE_PROCESSING,
               .entity         = UVC_GUID_LOGITECH_VIDEO_PIPE_V1,
               .selector       = 5,
               .size           = 8,
               .offset         = 0,
               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
       },
       {
               .id             = V4L2_CID_RAW_BITS_PER_PIXEL,
               .entity         = UVC_GUID_LOGITECH_VIDEO_PIPE_V1,
               .selector       = 8,
               .size           = 8,
               .offset         = 0,
               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
       },
       {
               .id             = V4L2_CID_LED1_MODE,
               .entity         = UVC_GUID_LOGITECH_PERIPHERAL,
               .selector       = 0x09,
               .size           = 2,
               .offset         = 8,
               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
               .menu_mask      = 0xF,
               .menu_mapping   = { 0, 1, 2, 3 },
               .menu_names     = { "Off", "On", "Blink", "Auto" },

       },
       {
               .id             = V4L2_CID_LED1_FREQUENCY,
               .entity         = UVC_GUID_LOGITECH_PERIPHERAL,
               .selector       = 0x09,
               .size           = 8,
               .offset         = 24,
               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
       },

This script has been used to generate the mappings. They were then
reformatted manually to follow the driver style.

import sys
import uuid
import re
import xml.etree.ElementTree as ET

def get_namespace(root):
    return re.match(r"\{.*\}", root.tag).group(0)

def get_single_guid(ns, constant):
    id = constant.find(ns + "id").text
    value = constant.find(ns + "value").text
    return (id, value)

def get_constants(ns, root):
    out = dict()
    for constant in root.iter(ns + "constant"):
        attr = constant.attrib
        if attr["type"] == "integer":
            id, value = get_single_guid(ns, constant)
            if id in out:
                print(f"dupe constant {id}")
            out[id] = value

    return out

def get_guids(ns, root):
    out = dict()
    for constant in root.iter(ns + "constant"):
        attr = constant.attrib
        if attr["type"] == "guid":
            id, value = get_single_guid(ns, constant)
            if id in out:
                print(f"dupe guid {id}")
            out[id] = value

    return out

def get_single_control(ns, control):
    out = {}
    for id in "entity", "selector", "index", "size", "description":
        v = control.find(ns + id)
        if v is None and id == "description":
            continue
        out[id] = v.text

    reqs = set()
    for r in control.find(ns + "requests"):
        reqs.add(r.text)
    out["requests"] = reqs

    return (control.attrib["id"], out)

def get_controls(ns, root):
    out = dict()
    for control in root.iter(ns + "control"):
        id, value = get_single_control(ns, control)
        if id in out:
            print(f"Dupe control id {id}")
        out[id] = value

    return out

def get_single_mapping(ns, mapping):
    out = {}
    out["name"] = mapping.find(ns + "name").text
    uvc = mapping.find(ns + "uvc")
    for id in "size", "offset", "uvc_type":
        out[id] = uvc.find(ns + id).text
    out["control_ref"] = uvc.find(ns + "control_ref").attrib["idref"]

    v4l2 = mapping.find(ns + "v4l2")
    for id in "id", "v4l2_type":
        out[id] = v4l2.find(ns + id).text

    menu = {}
    for entry in v4l2.iter(ns + "menu_entry"):
        menu[entry.attrib["name"]] = entry.attrib["value"]
    if menu:
        out["menu"] = menu

    return out

def get_mapping(ns, root):
    out = []
    for control in root.iter(ns + "mapping"):
        mapping = get_single_mapping(ns, control)
        out += [mapping]

    return out

def print_guids(guids):
    for g in guids:
        print(f"#define {g} \\")
        u_bytes = uuid.UUID(guids[g]).bytes_le
        u_bytes = [f"0x{b:02x}" for b in u_bytes]
        print("\t{ " + ", ".join(u_bytes) + " }")

def print_flags(flags):
    get_range = {"GET_MIN", "GET_DEF", "GET_MAX", "GET_CUR", "GET_RES"}
    if get_range.issubset(flags):
        flags -= get_range
        flags.add("GET_RANGE")

    flags = list(flags)
    flags.sort()
    out = ""
    for f in flags[:-1]:
        out += f"UVC_CTRL_FLAG_{f}\n\t\t\t\t| "

    out += f"UVC_CTRL_FLAG_{flags[-1]}"

    return out

def print_description(desc):
    print("/*")
    for line in desc.strip().splitlines():
        print(f" * {line.strip()}")
    print("*/")

def print_controls(controls, cons):
    for id in controls:
        c = controls[id]
        if "description" in c:
            print_description(c["description"])
        print(
            f"""\t{{
\t\t.entity\t\t= {c["entity"]},
\t\t.selector\t= {cons[c["selector"]]},
\t\t.index\t\t= {c["index"]},
\t\t.size\t\t= {c["size"]},
\t\t.flags\t\t= {print_flags(c["requests"])},
\t}},"""
        )

def menu_mapping_txt(menu):
    out = f"\n\t\t.menu_mask\t= 0x{((1<<len(menu))-1):X},\n"
    out += f"\t\t.menu_mapping\t= {{ {", ".join(menu.values())} }},\n"
    out += f"\t\t.menu_names\t= {{ \"{"\", \"".join(menu.keys())}\" }},\n"
    return out

def print_mappings(mappings, controls, cons):
    for m in mappings:
        c = controls[m["control_ref"]]

        if "menu" in m:
            menu_mapping = menu_mapping_txt(m["menu"])
        else:
            menu_mapping = ""
        print(
            f"""\t{{
\t\t.id\t\t= {m["id"]},
\t\t.entity\t\t= {c["entity"]},
\t\t.selector\t= {cons[c["selector"]]},
\t\t.size\t\t= {m["size"]},
\t\t.offset\t\t= {m["offset"]},
\t\t.v4l2_type\t= {m["v4l2_type"]},
\t\t.data_type\t= {m["uvc_type"]},{menu_mapping}
\t}},"""
        )

def print_code(guids, cons, controls, mappings):
    used_controls = set()
    for m in mappings:
        used_controls.add(m["control_ref"])

    used_guids = set()
    for c in used_controls:
        used_guids.add(controls[c]["entity"])

    print("\n######GUIDs#######\n")
    print_guids({id: guids[id] for id in guids if id in used_guids})
    print("\n######CONTROLS#######\n")
    print_controls({id: controls[id] for id in controls if id in used_controls}, cons)
    print("\n######MAPPINGS#######\n")
    print_mappings(mappings, controls, cons)
    # print(guids)
    # print(used_controls)

root = ET.fromstring(sys.stdin.read())
ns = get_namespace(root)
cons = get_constants(ns, root)
guids = get_guids(ns, root)
controls = get_controls(ns, root)
mappings = get_mapping(ns, root)
print_code(guids, cons, controls, mappings)

Cc: Manav Gautama <bandwidthcrunch@gmail.com>
Cc: Martin Rubli <martin_rubli@logitech.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 174 +++++++++++++++++++++++++++++++++++++++
 include/linux/usb/uvc.h          |   6 ++
 2 files changed, 180 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b9a3d9257a11..b6e020b41671 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -385,6 +385,99 @@ static const struct uvc_control_info uvc_ctrls[] = {
 				| UVC_CTRL_FLAG_GET_RANGE
 				| UVC_CTRL_FLAG_RESTORE,
 	},
+	/*
+	 * Allows the control of pan/tilt motor movements for camera models
+	 * that support mechanical pan/tilt.
+	 *
+	 * Bits 0 to 15 control pan, bits 16 to 31 control tilt.
+	 * The unit of the pan/tilt values is 1/64th of a degree and the
+	 * resolution is 1 degree.
+	 */
+	{
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 1,
+		.index          = 0,
+		.size           = 4,
+		.flags          = UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_MIN
+				| UVC_CTRL_FLAG_SET_CUR,
+	},
+	/*
+	 * Reset the pan/tilt motors to their original position for camera
+	 * models that support mechanical pan/tilt.
+	 *
+	 * Setting bit 0 resets the pan position.
+	 * Setting bit 1 resets the tilt position.
+	 *
+	 * Both bits can be set at the same time to reset both, pan and tilt,
+	 * at the same time.
+	 */
+	{
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 2,
+		.index          = 1,
+		.size           = 1,
+		.flags          = UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_MIN
+				| UVC_CTRL_FLAG_SET_CUR,
+	},
+	/*
+	 * Allows the control of focus motor movements for camera models that
+	 * support mechanical focus.
+	 *
+	 * Bits 0 to 7 allow selection of the desired lens position.
+	 * There are no physical units, instead, the focus range is spread over
+	 * 256 logical units with 0 representing infinity focus and 255 being
+	 * macro focus.
+	 */
+	{
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 3,
+		.index          = 2,
+		.size           = 6,
+		.flags          = UVC_CTRL_FLAG_GET_CUR
+				| UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_MIN
+				| UVC_CTRL_FLAG_SET_CUR,
+	},
+	/*
+	 * Allows the control of pan/tilt motor movements for camera models
+	 * that support mechanical pan/tilt.
+	 *
+	 * Bits 0 to 15 control pan, bits 16 to 31 control tilt.
+	 */
+	{
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 1,
+		.index          = 0,
+		.size           = 4,
+		.flags          = UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_MIN
+				| UVC_CTRL_FLAG_GET_RES
+				| UVC_CTRL_FLAG_SET_CUR,
+	},
+	/*
+	 * Reset the pan/tilt motors to their original position for camera
+	 * models that support mechanical pan/tilt.
+	 *
+	 * Setting bit 0 resets the pan position.
+	 * Setting bit 1 resets the tilt position.
+	 */
+	{
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 2,
+		.index          = 1,
+		.size           = 1,
+		.flags          = UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_MIN
+				| UVC_CTRL_FLAG_GET_RES
+				| UVC_CTRL_FLAG_SET_CUR,
+	},
 };
 
 static const u32 uvc_control_classes[] = {
@@ -1009,6 +1102,87 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.menu_mask	= BIT(V4L2_COLORFX_VIVID) |
 				  BIT(V4L2_COLORFX_NONE),
 	},
+	{
+		.id             = V4L2_CID_PAN_RELATIVE,
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 1,
+		.size           = 16,
+		.offset         = 0,
+		.v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+		.data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+	},
+	{
+		.id             = V4L2_CID_TILT_RELATIVE,
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 1,
+		.size           = 16,
+		.offset         = 16,
+		.v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+		.data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+	},
+	{
+		.id             = V4L2_CID_PAN_RESET,
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 2,
+		.size           = 1,
+		.offset         = 0,
+		.v4l2_type      = V4L2_CTRL_TYPE_BUTTON,
+		.data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+	},
+	{
+		.id             = V4L2_CID_TILT_RESET,
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 2,
+		.size           = 1,
+		.offset         = 1,
+		.v4l2_type      = V4L2_CTRL_TYPE_BUTTON,
+		.data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+	},
+	{
+		.id             = V4L2_CID_PAN_RELATIVE,
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 1,
+		.size           = 16,
+		.offset         = 0,
+		.v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+		.data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+	},
+	{
+		.id             = V4L2_CID_TILT_RELATIVE,
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 1,
+		.size           = 16,
+		.offset         = 16,
+		.v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+		.data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+	},
+	{
+		.id             = V4L2_CID_PAN_RESET,
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 2,
+		.size           = 1,
+		.offset         = 0,
+		.v4l2_type      = V4L2_CTRL_TYPE_BUTTON,
+		.data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+	},
+	{
+		.id             = V4L2_CID_TILT_RESET,
+		.entity         = UVC_GUID_LOGITECH_PERIPHERAL,
+		.selector       = 2,
+		.size           = 1,
+		.offset         = 1,
+		.v4l2_type      = V4L2_CTRL_TYPE_BUTTON,
+		.data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+	},
+	{
+		.id             = V4L2_CID_FOCUS_ABSOLUTE,
+		.entity         = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+		.selector       = 3,
+		.size           = 8,
+		.offset         = 0,
+		.v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+		.data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+	},
 };
 
 /* ------------------------------------------------------------------------
diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
index ea92ac623a45..dea23aabbad4 100644
--- a/include/linux/usb/uvc.h
+++ b/include/linux/usb/uvc.h
@@ -43,6 +43,12 @@
 #define UVC_GUID_MSXU_1_5 \
 	{0xdc, 0x95, 0x3f, 0x0f, 0x32, 0x26, 0x4e, 0x4c, \
 	 0x92, 0xc9, 0xa0, 0x47, 0x82, 0xf4, 0x3b, 0xc8}
+#define UVC_GUID_LOGITECH_MOTOR_CONTROL_V1 \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56 }
+#define UVC_GUID_LOGITECH_PERIPHERAL \
+	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
+	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
 
 /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
 #define UVC_MSXU_CONTROL_FOCUS			0x01

-- 
2.53.0.851.ga537e3e6e9-goog


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 2/4] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP
  2026-03-16 13:34 [PATCH v3 0/4] media: uvcvideo: Map known XU controls Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 1/4] media: uvcvideo: Import standard controls from uvcdynctrl Ricardo Ribalda
@ 2026-03-16 13:34 ` Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig Ricardo Ribalda
  3 siblings, 0 replies; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-16 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman
  Cc: linux-media, linux-kernel, linux-usb, Ricardo Ribalda

The UVCIOC_CTRL_MAP lets userspace create a mapping for a custom
control.

This mapping is usually created by the uvcdynctrl userspace utility. We
would like to get the mappings into the driver instead.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 Documentation/userspace-api/media/drivers/uvcvideo.rst | 2 ++
 drivers/media/usb/uvc/uvc_v4l2.c                       | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index dbb30ad389ae..b09d2f8ba66e 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -109,6 +109,8 @@ IOCTL reference
 UVCIOC_CTRL_MAP - Map a UVC control to a V4L2 control
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+**This IOCTL is deprecated and will be eventually removed**
+
 Argument: struct uvc_xu_control_mapping
 
 **Description**:
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index d5860661c115..f9049e9c0d3a 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1046,6 +1046,8 @@ static long uvc_ioctl_default(struct file *file, void *priv, bool valid_prio,
 	switch (cmd) {
 	/* Dynamic controls. */
 	case UVCIOC_CTRL_MAP:
+		pr_warn_once("uvcvideo: " DEPRECATED
+			     "UVCIOC_CTRL_MAP ioctl will be eventually removed.\n");
 		return uvc_ioctl_xu_ctrl_map(chain, arg);
 
 	case UVCIOC_CTRL_QUERY:
@@ -1160,6 +1162,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
 
 	switch (cmd) {
 	case UVCIOC_CTRL_MAP32:
+		pr_warn_once("uvcvideo: " DEPRECATED
+			     "UVCIOC_CTRL_MAP32 ioctl will be eventually removed.\n");
 		ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
 		if (ret)
 			break;

-- 
2.53.0.851.ga537e3e6e9-goog


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-16 13:34 [PATCH v3 0/4] media: uvcvideo: Map known XU controls Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 1/4] media: uvcvideo: Import standard controls from uvcdynctrl Ricardo Ribalda
  2026-03-16 13:34 ` [PATCH v3 2/4] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP Ricardo Ribalda
@ 2026-03-16 13:34 ` Ricardo Ribalda
  2026-03-19  0:36   ` Michal Pecio
  2026-03-16 13:34 ` [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig Ricardo Ribalda
  3 siblings, 1 reply; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-16 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman
  Cc: linux-media, linux-kernel, linux-usb, Ricardo Ribalda

Some camera modules have XU controls that can configure the behaviour of
the privacy LED.

Block mapping of those controls, unless the module is configured with
a new parameter: allow_privacy_override.

This is just an interim solution. Based on the users feedback, we will
either put the privacy controls behind a CONFIG option, or completely
block them.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
 drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
 drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
 drivers/media/usb/uvc/uvcvideo.h   |  2 ++
 include/linux/usb/uvc.h            |  4 ++++
 5 files changed, 71 insertions(+)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b6e020b41671..3ca108b83f1d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
 	return ret;
 }
 
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
+{
+	/*
+	 * This list is not exhaustive, it is a best effort to block access to
+	 * non documented controls that can affect user's privacy.
+	 */
+	struct privacy_control {
+		u8 entity[16];
+		u8 selector;
+	} privacy_control[] = {
+		{
+			.entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
+			.selector = 1,
+		},
+		{
+			.entity = UVC_GUID_LOGITECH_PERIPHERAL,
+			.selector = 9,
+		},
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
+		if (!memcmp(entity, privacy_control[i].entity, 16) &&
+		    selector == privacy_control[i].selector)
+			return true;
+
+	return false;
+}
+
 int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	struct uvc_xu_control_query *xqry)
 {
@@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 		return -ENOENT;
 	}
 
+	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
+	    !uvc_allow_privacy_override_param) {
+		dev_warn_once(&chain->dev->intf->dev,
+			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
+		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
+			entity->guid, xqry->selector);
+		return -EACCES;
+	}
+
 	if (mutex_lock_interruptible(&chain->ctrl_mutex))
 		return -ERESTARTSYS;
 
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index b0ca81d924b6..74c9dea29d36 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
 static unsigned int uvc_quirks_param = -1;
 unsigned int uvc_dbg_param;
 unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+bool uvc_allow_privacy_override_param;
 
 static struct usb_driver uvc_driver;
 
@@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
 module_param_named(timeout, uvc_timeout_param, uint, 0644);
 MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
 
+static int param_set_privacy(const char *val, const struct kernel_param *kp)
+{
+	pr_warn_once("uvcvideo: " DEPRECATED
+		     "allow_privacy_override parameter will be eventually removed.\n");
+	return param_set_bool(val, kp);
+}
+
+static const struct kernel_param_ops param_ops_privacy = {
+	.set = param_set_privacy,
+	.get = param_get_bool,
+};
+
+param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
+module_param_cb(allow_privacy_override, &param_ops_privacy,
+		&uvc_allow_privacy_override_param, 0644);
+__MODULE_PARM_TYPE(allow_privacy_override, "bool");
+MODULE_PARM_DESC(allow_privacy_override,
+		 "Allow access to privacy related controls");
+
 /* ------------------------------------------------------------------------
  * Driver initialization and cleanup
  */
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f9049e9c0d3a..6d4f027c8402 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
 		return -EINVAL;
 	}
 
+	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
+	    !uvc_allow_privacy_override_param) {
+		dev_warn_once(&chain->dev->intf->dev,
+			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
+		return -EACCES;
+	}
+
 	map = kzalloc_obj(*map);
 	if (map == NULL)
 		return -ENOMEM;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 8480d65ecb85..362110d58ca3 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_dbg_param;
 extern unsigned int uvc_timeout_param;
 extern unsigned int uvc_hw_timestamps_param;
+extern bool uvc_allow_privacy_override_param;
 
 #define uvc_dbg(_dev, flag, fmt, ...)					\
 do {									\
@@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 		      struct uvc_xu_control_query *xqry);
 
 void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
 
 /* Utility functions */
 struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
index dea23aabbad4..70c2a7d25236 100644
--- a/include/linux/usb/uvc.h
+++ b/include/linux/usb/uvc.h
@@ -49,6 +49,10 @@
 #define UVC_GUID_LOGITECH_PERIPHERAL \
 	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
 	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
+#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
+	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
+
 
 /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
 #define UVC_MSXU_CONTROL_FOCUS			0x01

-- 
2.53.0.851.ga537e3e6e9-goog


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig
  2026-03-16 13:34 [PATCH v3 0/4] media: uvcvideo: Map known XU controls Ricardo Ribalda
                   ` (2 preceding siblings ...)
  2026-03-16 13:34 ` [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter Ricardo Ribalda
@ 2026-03-16 13:34 ` Ricardo Ribalda
  2026-03-18 14:16   ` Greg Kroah-Hartman
  3 siblings, 1 reply; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-16 13:34 UTC (permalink / raw)
  To: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman
  Cc: linux-media, linux-kernel, linux-usb, Ricardo Ribalda

This patch is just shared for discussion purposes! Do not land.

In a perfect world, after a deprecation process, we will be able to
remove allow_privacy_override and block all privacy related controls.

If there is any usecase out in the field that resists, we shall move it
into a Kconfig.

This patch shows how the transition to Kconfig can look.

Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
---
 drivers/media/usb/uvc/Kconfig      | 11 +++++++++++
 drivers/media/usb/uvc/uvc_ctrl.c   |  4 ++--
 drivers/media/usb/uvc/uvc_driver.c | 20 --------------------
 drivers/media/usb/uvc/uvc_v4l2.c   |  4 ++--
 drivers/media/usb/uvc/uvcvideo.h   |  1 -
 5 files changed, 15 insertions(+), 25 deletions(-)

diff --git a/drivers/media/usb/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig
index 579532272fd6..7c0f2260d135 100644
--- a/drivers/media/usb/uvc/Kconfig
+++ b/drivers/media/usb/uvc/Kconfig
@@ -20,3 +20,14 @@ config USB_VIDEO_CLASS_INPUT_EVDEV
 	  to report button events.
 
 	  If you are in doubt, say Y.
+
+config USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE
+	bool "Allow overriding the privacy controls"
+	default n
+	depends on USB_VIDEO_CLASS && BROKEN
+	help
+	  If this option is enabled, the privacy related controls, such as
+	  the ones controlling the privacy LED will be accessible from
+	  userspace.
+
+	  If you are in doubt, say N.
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 3ca108b83f1d..c9c195974765 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -3075,9 +3075,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 	}
 
 	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
-	    !uvc_allow_privacy_override_param) {
+	    !IS_ENABLED(CONFIG_USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE)) {
 		dev_warn_once(&chain->dev->intf->dev,
-			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
+			      "Privacy related controls can only be accessed if CONFIG_USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE is true\n");
 		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
 			entity->guid, xqry->selector);
 		return -EACCES;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 74c9dea29d36..b0ca81d924b6 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -36,7 +36,6 @@ unsigned int uvc_no_drop_param = 1;
 static unsigned int uvc_quirks_param = -1;
 unsigned int uvc_dbg_param;
 unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
-bool uvc_allow_privacy_override_param;
 
 static struct usb_driver uvc_driver;
 
@@ -2506,25 +2505,6 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
 module_param_named(timeout, uvc_timeout_param, uint, 0644);
 MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
 
-static int param_set_privacy(const char *val, const struct kernel_param *kp)
-{
-	pr_warn_once("uvcvideo: " DEPRECATED
-		     "allow_privacy_override parameter will be eventually removed.\n");
-	return param_set_bool(val, kp);
-}
-
-static const struct kernel_param_ops param_ops_privacy = {
-	.set = param_set_privacy,
-	.get = param_get_bool,
-};
-
-param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
-module_param_cb(allow_privacy_override, &param_ops_privacy,
-		&uvc_allow_privacy_override_param, 0644);
-__MODULE_PARM_TYPE(allow_privacy_override, "bool");
-MODULE_PARM_DESC(allow_privacy_override,
-		 "Allow access to privacy related controls");
-
 /* ------------------------------------------------------------------------
  * Driver initialization and cleanup
  */
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 6d4f027c8402..f83abbd13aca 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -134,9 +134,9 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
 	}
 
 	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
-	    !uvc_allow_privacy_override_param) {
+	    !IS_ENABLED(CONFIG_USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE)) {
 		dev_warn_once(&chain->dev->intf->dev,
-			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
+			      "Privacy related controls can only be mapped if CONFIG_USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE is true\n");
 		return -EACCES;
 	}
 
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 362110d58ca3..2898384d5f78 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -664,7 +664,6 @@ extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_dbg_param;
 extern unsigned int uvc_timeout_param;
 extern unsigned int uvc_hw_timestamps_param;
-extern bool uvc_allow_privacy_override_param;
 
 #define uvc_dbg(_dev, flag, fmt, ...)					\
 do {									\

-- 
2.53.0.851.ga537e3e6e9-goog


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig
  2026-03-16 13:34 ` [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig Ricardo Ribalda
@ 2026-03-18 14:16   ` Greg Kroah-Hartman
  2026-03-18 14:57     ` Ricardo Ribalda
  0 siblings, 1 reply; 15+ messages in thread
From: Greg Kroah-Hartman @ 2026-03-18 14:16 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	linux-media, linux-kernel, linux-usb

On Mon, Mar 16, 2026 at 01:34:47PM +0000, Ricardo Ribalda wrote:
> This patch is just shared for discussion purposes! Do not land.
> 
> In a perfect world, after a deprecation process, we will be able to
> remove allow_privacy_override and block all privacy related controls.

Why add something you are only going to remove in the future?  What has
changed to require this now, and will change in the future to make it
not needed?

> If there is any usecase out in the field that resists, we shall move it
> into a Kconfig.

What does this mean?  How will anyone know to "resist"?

> This patch shows how the transition to Kconfig can look.

I'm confused as to what you want to do here...

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig
  2026-03-18 14:16   ` Greg Kroah-Hartman
@ 2026-03-18 14:57     ` Ricardo Ribalda
  2026-03-19 11:50       ` Gergo Koteles
  0 siblings, 1 reply; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-18 14:57 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	linux-media, linux-kernel, linux-usb

Hi Greg

On Wed, 18 Mar 2026 at 15:17, Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> On Mon, Mar 16, 2026 at 01:34:47PM +0000, Ricardo Ribalda wrote:
> > This patch is just shared for discussion purposes! Do not land.
> >
> > In a perfect world, after a deprecation process, we will be able to
> > remove allow_privacy_override and block all privacy related controls.
>
> Why add something you are only going to remove in the future?  What has
> changed to require this now, and will change in the future to make it
> not needed?

Currently, any application with camera access can manipulate the
privacy LED. I believe this is a security flaw; ideally, the kernel
should block all such controls by default.

However, blocking these controls immediately might be seen as a
regression for certain users. I added allow_privacy_override to:
- Prevent breaking existing workflows immediately upon a kernel update.
- Give users time to report why they still need manual LED control.

The goal is to gather these use cases over the next 1–2 years. Once we
understand the legitimate needs, we can either implement a proper
specialized mechanism for them or move the toggle to a Kconfig option
for those who explicitly need to opt-in to the old behavior or simply
remove the toggle altogether.

For the record, identified use cases so far:
- Old hardware with red LEDs that reflect on glasses. (Likely a dying niche).
- Using cameras as baby monitors where the LED disturbs sleep.
(Arguably solvable with a piece of tape on the LED, but still a
reported use case).

>
> > If there is any usecase out in the field that resists, we shall move it
> > into a Kconfig.
>
> What does this mean?  How will anyone know to "resist"?

My phrasing was poor, sorry about that. What I mean is: if, after a
deprecation period, we find there are still legitimate reasons to
allow LED overrides, we will move the functionality behind a Kconfig
option (e.g., USB_VIDEO_CLASS_ALLOW_PRIVACY_OVERRIDE) or other option.
If no one reports a need for it, we simply remove the override
capability entirely.

>
> > This patch shows how the transition to Kconfig can look.
>
> I'm confused as to what you want to do here...

This patch is just a RFC to demonstrate the final state if we decide a
Kconfig option is necessary. The actual plan is to land patches 1-3
first, wait for feedback, and only then decide if we need the Kconfig
transition or a full removal or something else.

>
> greg k-h



--
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-16 13:34 ` [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter Ricardo Ribalda
@ 2026-03-19  0:36   ` Michal Pecio
  2026-03-19  9:56     ` Ricardo Ribalda
  0 siblings, 1 reply; 15+ messages in thread
From: Michal Pecio @ 2026-03-19  0:36 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

On Mon, 16 Mar 2026 13:34:46 +0000, Ricardo Ribalda wrote:
> Some camera modules have XU controls that can configure the behaviour of
> the privacy LED.
> 
> Block mapping of those controls, unless the module is configured with
> a new parameter: allow_privacy_override.
> 
> This is just an interim solution. Based on the users feedback, we will
> either put the privacy controls behind a CONFIG option, or completely
> block them.

What feedback do you expect to get?

Users will one day see their setup broken.
They will curse you and jump through the hoops you set up.
Next year they will see their setup broken completely.
They will curse again and wish you all pain, but *after* the fact.

> Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
>  drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
>  drivers/media/usb/uvc/uvcvideo.h   |  2 ++
>  include/linux/usb/uvc.h            |  4 ++++
>  5 files changed, 71 insertions(+)

This doesn't seem to cover libusb, VM guests and such.

What's even the attack vector? It has to be full remote code execution.
And it's just an LED, when you see it turn on somebody already has your
mugshot, if you notice at all. And the mugshot isn't your worst worry.

> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index b6e020b41671..3ca108b83f1d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
>  	return ret;
>  }
>  
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
> +{
> +	/*
> +	 * This list is not exhaustive, it is a best effort to block access to
> +	 * non documented controls that can affect user's privacy.
> +	 */

So it's not removal of some controversial feature, but 3KB of extra
code in everybody's kernel (I just applied this patch) and a forever
game of whack-a-mole with HW vendors? They will win...

You will blacklist features found by legitimate users and shared on
public forums, while hackers will keep their findings to themselves.
Assuming that there are any who even care.

> +	struct privacy_control {
> +		u8 entity[16];
> +		u8 selector;
> +	} privacy_control[] = {
> +		{
> +			.entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
> +			.selector = 1,
> +		},
> +		{
> +			.entity = UVC_GUID_LOGITECH_PERIPHERAL,
> +			.selector = 9,
> +		},
> +	};
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
> +		if (!memcmp(entity, privacy_control[i].entity, 16) &&
> +		    selector == privacy_control[i].selector)
> +			return true;
> +
> +	return false;
> +}
> +
>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  	struct uvc_xu_control_query *xqry)
>  {
> @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		return -ENOENT;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
> +		uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
> +			entity->guid, xqry->selector);
> +		return -EACCES;
> +	}
> +
>  	if (mutex_lock_interruptible(&chain->ctrl_mutex))
>  		return -ERESTARTSYS;
>  
> diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> index b0ca81d924b6..74c9dea29d36 100644
> --- a/drivers/media/usb/uvc/uvc_driver.c
> +++ b/drivers/media/usb/uvc/uvc_driver.c
> @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
>  static unsigned int uvc_quirks_param = -1;
>  unsigned int uvc_dbg_param;
>  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
> +bool uvc_allow_privacy_override_param;
>  
>  static struct usb_driver uvc_driver;
>  
> @@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
>  module_param_named(timeout, uvc_timeout_param, uint, 0644);
>  MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
>  
> +static int param_set_privacy(const char *val, const struct kernel_param *kp)
> +{
> +	pr_warn_once("uvcvideo: " DEPRECATED
> +		     "allow_privacy_override parameter will be eventually removed.\n");
> +	return param_set_bool(val, kp);
> +}
> +
> +static const struct kernel_param_ops param_ops_privacy = {
> +	.set = param_set_privacy,
> +	.get = param_get_bool,
> +};
> +
> +param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
> +module_param_cb(allow_privacy_override, &param_ops_privacy,
> +		&uvc_allow_privacy_override_param, 0644);
> +__MODULE_PARM_TYPE(allow_privacy_override, "bool");
> +MODULE_PARM_DESC(allow_privacy_override,
> +		 "Allow access to privacy related controls");
> +
>  /* ------------------------------------------------------------------------
>   * Driver initialization and cleanup
>   */
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index f9049e9c0d3a..6d4f027c8402 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
>  		return -EINVAL;
>  	}
>  
> +	if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
> +	    !uvc_allow_privacy_override_param) {
> +		dev_warn_once(&chain->dev->intf->dev,
> +			      "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
> +		return -EACCES;
> +	}
> +
>  	map = kzalloc_obj(*map);
>  	if (map == NULL)
>  		return -ENOMEM;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 8480d65ecb85..362110d58ca3 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
>  extern unsigned int uvc_dbg_param;
>  extern unsigned int uvc_timeout_param;
>  extern unsigned int uvc_hw_timestamps_param;
> +extern bool uvc_allow_privacy_override_param;
>  
>  #define uvc_dbg(_dev, flag, fmt, ...)					\
>  do {									\
> @@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>  		      struct uvc_xu_control_query *xqry);
>  
>  void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
>  
>  /* Utility functions */
>  struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
> index dea23aabbad4..70c2a7d25236 100644
> --- a/include/linux/usb/uvc.h
> +++ b/include/linux/usb/uvc.h
> @@ -49,6 +49,10 @@
>  #define UVC_GUID_LOGITECH_PERIPHERAL \
>  	{0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
>  	 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
> +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
> +	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
> +	 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
> +
>  
>  /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
>  #define UVC_MSXU_CONTROL_FOCUS			0x01
> 
> -- 
> 2.53.0.851.ga537e3e6e9-goog
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-19  0:36   ` Michal Pecio
@ 2026-03-19  9:56     ` Ricardo Ribalda
  2026-03-19 11:08       ` Michal Pecio
  0 siblings, 1 reply; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-19  9:56 UTC (permalink / raw)
  To: Michal Pecio
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

Hi Michal

On Thu, 19 Mar 2026 at 01:37, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Mon, 16 Mar 2026 13:34:46 +0000, Ricardo Ribalda wrote:
> > Some camera modules have XU controls that can configure the behaviour of
> > the privacy LED.
> >
> > Block mapping of those controls, unless the module is configured with
> > a new parameter: allow_privacy_override.
> >
> > This is just an interim solution. Based on the users feedback, we will
> > either put the privacy controls behind a CONFIG option, or completely
> > block them.
>
> What feedback do you expect to get?

I want to identify the valid usecases for overriding the privacy LEDs.

>
> Users will one day see their setup broken.
> They will curse you and jump through the hoops you set up.
> Next year they will see their setup broken completely.
> They will curse again and wish you all pain, but *after* the fact.

The goal of the deprecation period is exactly this: to trigger a
conversation before a permanent block. If a user relies on this, they
can report it now. We can then decide if we need a specialized API for
their use case or a Kconfig option, rather than leaving the current
"anyone can turn off the privacy LED" status quo.


>
> > Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c   | 38 ++++++++++++++++++++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_driver.c | 20 ++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_v4l2.c   |  7 +++++++
> >  drivers/media/usb/uvc/uvcvideo.h   |  2 ++
> >  include/linux/usb/uvc.h            |  4 ++++
> >  5 files changed, 71 insertions(+)
>
> This doesn't seem to cover libusb, VM guests and such.

For libusb and VM guests to work you need to unbind the uvc driver.
Only privileged users can do that.
Today, any user with camera access can disable the privacy LED.

>
> What's even the attack vector? It has to be full remote code execution.
> And it's just an LED, when you see it turn on somebody already has your
> mugshot, if you notice at all. And the mugshot isn't your worst worry.

The attack vector is that an app with camera access, like your
browser, can record you when you don't want to be recorded.
The LED will be a signal that something is happening.

Imagine that you install a Flatpak for live streaming. Assuming the
Flatpak is properly sandboxed, remote code execution is less worrisome
than the app spying on you.

>
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index b6e020b41671..3ca108b83f1d 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -3001,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
> >       return ret;
> >  }
> >
> > +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
> > +{
> > +     /*
> > +      * This list is not exhaustive, it is a best effort to block access to
> > +      * non documented controls that can affect user's privacy.
> > +      */
>
> So it's not removal of some controversial feature, but 3KB of extra
> code in everybody's kernel (I just applied this patch) and a forever
> game of whack-a-mole with HW vendors? They will win...

Maybe I meassured it wrong. But I can only account for 1.3 KiB

$ size drivers/media/usb/uvc/uvcvideo-without.ko
   text    data     bss     dec     hex filename
 115974    3748      88  119810   1d402 drivers/media/usb/uvc/uvcvideo.ko

$ size drivers/media/usb/uvc/uvcvideo-with.ko
   text    data     bss     dec     hex filename
 117315    3767      88  121170   1d952 drivers/media/usb/uvc/uvcvideo.ko

I see no need for vendors to hide these features, they simply added
them because an OEM thought it was a nice feature to have, or because
they left them as hardware debug features.

>
> You will blacklist features found by legitimate users and shared on
> public forums, while hackers will keep their findings to themselves.
> Assuming that there are any who even care.

If a legitimate user needs a feature, this patch gives them a way to
keep using it (allow_privacy_override) while notifying us. This allows
the community to find a better, safer way to support that specific
need without leaving the door wide open for everyone else.

>
> > +     struct privacy_control {
> > +             u8 entity[16];
> > +             u8 selector;
> > +     } privacy_control[] = {
> > +             {
> > +                     .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
> > +                     .selector = 1,
> > +             },
> > +             {
> > +                     .entity = UVC_GUID_LOGITECH_PERIPHERAL,
> > +                     .selector = 9,
> > +             },
> > +     };
> > +     int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
> > +             if (!memcmp(entity, privacy_control[i].entity, 16) &&
> > +                 selector == privacy_control[i].selector)
> > +                     return true;
> > +
> > +     return false;
> > +}
> > +
> >  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >       struct uvc_xu_control_query *xqry)
> >  {
> > @@ -3045,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >               return -ENOENT;
> >       }
> >
> > +     if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
> > +         !uvc_allow_privacy_override_param) {
> > +             dev_warn_once(&chain->dev->intf->dev,
> > +                           "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
> > +             uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
> > +                     entity->guid, xqry->selector);
> > +             return -EACCES;
> > +     }
> > +
> >       if (mutex_lock_interruptible(&chain->ctrl_mutex))
> >               return -ERESTARTSYS;
> >
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
> > index b0ca81d924b6..74c9dea29d36 100644
> > --- a/drivers/media/usb/uvc/uvc_driver.c
> > +++ b/drivers/media/usb/uvc/uvc_driver.c
> > @@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
> >  static unsigned int uvc_quirks_param = -1;
> >  unsigned int uvc_dbg_param;
> >  unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
> > +bool uvc_allow_privacy_override_param;
> >
> >  static struct usb_driver uvc_driver;
> >
> > @@ -2505,6 +2506,25 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
> >  module_param_named(timeout, uvc_timeout_param, uint, 0644);
> >  MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
> >
> > +static int param_set_privacy(const char *val, const struct kernel_param *kp)
> > +{
> > +     pr_warn_once("uvcvideo: " DEPRECATED
> > +                  "allow_privacy_override parameter will be eventually removed.\n");
> > +     return param_set_bool(val, kp);
> > +}
> > +
> > +static const struct kernel_param_ops param_ops_privacy = {
> > +     .set = param_set_privacy,
> > +     .get = param_get_bool,
> > +};
> > +
> > +param_check_bool(allow_privacy_override, &uvc_allow_privacy_override_param);
> > +module_param_cb(allow_privacy_override, &param_ops_privacy,
> > +             &uvc_allow_privacy_override_param, 0644);
> > +__MODULE_PARM_TYPE(allow_privacy_override, "bool");
> > +MODULE_PARM_DESC(allow_privacy_override,
> > +              "Allow access to privacy related controls");
> > +
> >  /* ------------------------------------------------------------------------
> >   * Driver initialization and cleanup
> >   */
> > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > index f9049e9c0d3a..6d4f027c8402 100644
> > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > @@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
> >               return -EINVAL;
> >       }
> >
> > +     if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
> > +         !uvc_allow_privacy_override_param) {
> > +             dev_warn_once(&chain->dev->intf->dev,
> > +                           "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
> > +             return -EACCES;
> > +     }
> > +
> >       map = kzalloc_obj(*map);
> >       if (map == NULL)
> >               return -ENOMEM;
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 8480d65ecb85..362110d58ca3 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -664,6 +664,7 @@ extern unsigned int uvc_no_drop_param;
> >  extern unsigned int uvc_dbg_param;
> >  extern unsigned int uvc_timeout_param;
> >  extern unsigned int uvc_hw_timestamps_param;
> > +extern bool uvc_allow_privacy_override_param;
> >
> >  #define uvc_dbg(_dev, flag, fmt, ...)                                        \
> >  do {                                                                 \
> > @@ -794,6 +795,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >                     struct uvc_xu_control_query *xqry);
> >
> >  void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
> > +bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
> >
> >  /* Utility functions */
> >  struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
> > diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
> > index dea23aabbad4..70c2a7d25236 100644
> > --- a/include/linux/usb/uvc.h
> > +++ b/include/linux/usb/uvc.h
> > @@ -49,6 +49,10 @@
> >  #define UVC_GUID_LOGITECH_PERIPHERAL \
> >       {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
> >        0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
> > +#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
> > +     {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
> > +      0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
> > +
> >
> >  /* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
> >  #define UVC_MSXU_CONTROL_FOCUS                       0x01
> >
> > --
> > 2.53.0.851.ga537e3e6e9-goog
> >



--
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-19  9:56     ` Ricardo Ribalda
@ 2026-03-19 11:08       ` Michal Pecio
  2026-03-19 11:43         ` Ricardo Ribalda
  0 siblings, 1 reply; 15+ messages in thread
From: Michal Pecio @ 2026-03-19 11:08 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

On Thu, 19 Mar 2026 10:56:59 +0100, Ricardo Ribalda wrote:
> The goal of the deprecation period is exactly this: to trigger a
> conversation before a permanent block.

Most users will just curse and edit their /etc/modprobe.conf. They may
post a rant on some distro forum. I suspect no one will monitor this.

> We can then decide if we need a specialized API for their use case or
> a Kconfig option, rather than leaving the current "anyone can turn
> off the privacy LED" status quo.

Why not just add the specialized API right away?

I believe users affected by this regression are already known,
ISTR some negative response to previous iterations of this patch.

Kconfig option sounds crazy, who would want to rebuild the kernel
for this? Depending on BROKEN is double crazy.

> The attack vector is that an app with camera access, like your
> browser, can record you when you don't want to be recorded.
> The LED will be a signal that something is happening.
> 
> Imagine that you install a Flatpak for live streaming. Assuming the
> Flatpak is properly sandboxed, remote code execution is less worrisome
> than the app spying on you.

Theoretically yes. But also nobody should rely on those LEDs.
People who care ask HW vendors for physical switches or disconnect
the camera while not in use. I have seen black tape on laptop lids.

Are there more owners of affected hardware who want this code than
those who don't? Maybe it could be a Kconfig option for them :)

Most of my USB cameras don't even have activity LEDs.

> > So it's not removal of some controversial feature, but 3KB of extra
> > code in everybody's kernel (I just applied this patch) and a forever
> > game of whack-a-mole with HW vendors? They will win...  
> 
> Maybe I meassured it wrong. But I can only account for 1.3 KiB

I simply ran stat uvcvideo.ko and calculated difference.
Could be a matter of different kernel configs.

> I see no need for vendors to hide these features, they simply added
> them because an OEM thought it was a nice feature to have, or because
> they left them as hardware debug features.

But how will the kernel know about those random debug backdoors?
It just seems that whatever is discovered by users and becomes popular
enough to reach linux-media, will be getting blacklisted and broken.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-19 11:08       ` Michal Pecio
@ 2026-03-19 11:43         ` Ricardo Ribalda
  2026-03-24 12:07           ` Michal Pecio
  0 siblings, 1 reply; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-19 11:43 UTC (permalink / raw)
  To: Michal Pecio
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

Hi Michal

On Thu, 19 Mar 2026 at 12:09, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Thu, 19 Mar 2026 10:56:59 +0100, Ricardo Ribalda wrote:
> > The goal of the deprecation period is exactly this: to trigger a
> > conversation before a permanent block.
>
> Most users will just curse and edit their /etc/modprobe.conf. They may
> post a rant on some distro forum. I suspect no one will monitor this.
>
> > We can then decide if we need a specialized API for their use case or
> > a Kconfig option, rather than leaving the current "anyone can turn
> > off the privacy LED" status quo.
>
> Why not just add the specialized API right away?

We don't know the exact use cases yet, and I do not want to design an
API without understanding the users for it.

At this moment, we have only identified these usecases:

- Disabling the LED to avoid reflections in glasses. (This is
generally a non-issue with modern hardware).
- Baby monitors. (I would argue that physical tape is the correct
solution for a sleep-disturbing light).

>
> I believe users affected by this regression are already known,
> ISTR some negative response to previous iterations of this patch.
>
> Kconfig option sounds crazy, who would want to rebuild the kernel
> for this? Depending on BROKEN is double crazy.

I am not set on the final implementation yet; it is exactly the kind
of topic we should discuss at a media summit.

>
> > The attack vector is that an app with camera access, like your
> > browser, can record you when you don't want to be recorded.
> > The LED will be a signal that something is happening.
> >
> > Imagine that you install a Flatpak for live streaming. Assuming the
> > Flatpak is properly sandboxed, remote code execution is less worrisome
> > than the app spying on you.
>
> Theoretically yes. But also nobody should rely on those LEDs.
> People who care ask HW vendors for physical switches or disconnect
> the camera while not in use. I have seen black tape on laptop lids.

I rely on my LEDs. I know they are wired to the sensor power supply,
so the LED is definitely on when the camera is in use.
I want all users to be able to trust their LEDs like I do.

>
> Are there more owners of affected hardware who want this code than
> those who don't? Maybe it could be a Kconfig option for them :)

I believe the majority of users prefer a system that is "secure by
default." Most people expect that if the LED is off, the camera is
off.

>
> Most of my USB cameras don't even have activity LEDs.
>
> > > So it's not removal of some controversial feature, but 3KB of extra
> > > code in everybody's kernel (I just applied this patch) and a forever
> > > game of whack-a-mole with HW vendors? They will win...
> >
> > Maybe I meassured it wrong. But I can only account for 1.3 KiB
>
> I simply ran stat uvcvideo.ko and calculated difference.
> Could be a matter of different kernel configs.
>
> > I see no need for vendors to hide these features, they simply added
> > them because an OEM thought it was a nice feature to have, or because
> > they left them as hardware debug features.
>
> But how will the kernel know about those random debug backdoors?
> It just seems that whatever is discovered by users and becomes popular
> enough to reach linux-media, will be getting blacklisted and broken.
>

I prefer to say "filtered" rather than "broken." It’s a matter of
perspective: we are filtering out non-standard controls that undermine
user privacy. While we might not catch every debug backdoor
immediately, setting a policy and blocking known overrides is a
significant step and also sends a strong message to vendors.

Best regards!


-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig
  2026-03-18 14:57     ` Ricardo Ribalda
@ 2026-03-19 11:50       ` Gergo Koteles
  2026-03-19 12:06         ` Ricardo Ribalda
  0 siblings, 1 reply; 15+ messages in thread
From: Gergo Koteles @ 2026-03-19 11:50 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Greg Kroah-Hartman, Laurent Pinchart, Hans de Goede,
	Mauro Carvalho Chehab, linux-media, linux-kernel, linux-usb

Hi Ricardo,

On Wed, 2026-03-18 at 15:57 +0100, Ricardo Ribalda wrote:
> Hi Greg
> 
> On Wed, 18 Mar 2026 at 15:17, Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> > 
> > On Mon, Mar 16, 2026 at 01:34:47PM +0000, Ricardo Ribalda wrote:
> > > This patch is just shared for discussion purposes! Do not land.
> > > 
> > > In a perfect world, after a deprecation process, we will be able to
> > > remove allow_privacy_override and block all privacy related controls.
> > 
> > Why add something you are only going to remove in the future?  What has
> > changed to require this now, and will change in the future to make it
> > not needed?
> 
> Currently, any application with camera access can manipulate the
> privacy LED. I believe this is a security flaw; ideally, the kernel
> should block all such controls by default.
> 
> However, blocking these controls immediately might be seen as a
> regression for certain users. I added allow_privacy_override to:
> - Prevent breaking existing workflows immediately upon a kernel update.
> - Give users time to report why they still need manual LED control.
> 
> The goal is to gather these use cases over the next 1–2 years. Once we
> understand the legitimate needs, we can either implement a proper
> specialized mechanism for them or move the toggle to a Kconfig option
> for those who explicitly need to opt-in to the old behavior or simply
> remove the toggle altogether.
> 
> For the record, identified use cases so far:
> - Old hardware with red LEDs that reflect on glasses. (Likely a dying niche).

Older Logitech cameras, like the c920, have fairly large LEDs that can
be reflective, and it's hard to cover them up aesthetically. Laurent
wrote [1] that's why Logitech added this functionality a long time ago.

You can find old Logitech software for Windows that still allows you to
turn off the LEDs [2]. I tried it in a Win10 VM and it really works.

The c920 is a very popular camera, still available in stores today.
Amazon says it sells 12k of them per month [3].

1:
https://lore.kernel.org/all/20251119041907.GH10711@pendragon.ideasonboard.com/

2: https://www.reddit.com/r/logitech/comments/v641x6/comment/nr22zo7/

3: https://www.amazon.com/s?k=logitech+c920

> - Using cameras as baby monitors where the LED disturbs sleep.

And pet cameras and surveillance cameras don't need LEDs either.

> (Arguably solvable with a piece of tape on the LED, but still a
> reported use case).
> 

Furthermore, if we filter these two Logitech XUs, we could then add the
similar XUs of the camera modules in laptops and this could be an ever-
growing list.

> 
Best regards,
Gergo Koteles

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig
  2026-03-19 11:50       ` Gergo Koteles
@ 2026-03-19 12:06         ` Ricardo Ribalda
  0 siblings, 0 replies; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-19 12:06 UTC (permalink / raw)
  To: Gergo Koteles
  Cc: Greg Kroah-Hartman, Laurent Pinchart, Hans de Goede,
	Mauro Carvalho Chehab, linux-media, linux-kernel, linux-usb

Hi Gergo

On Thu, 19 Mar 2026 at 12:50, Gergo Koteles <soyer@irl.hu> wrote:
>
> Hi Ricardo,
>
> On Wed, 2026-03-18 at 15:57 +0100, Ricardo Ribalda wrote:
> > Hi Greg
> >
> > On Wed, 18 Mar 2026 at 15:17, Greg Kroah-Hartman
> > <gregkh@linuxfoundation.org> wrote:
> > >
> > > On Mon, Mar 16, 2026 at 01:34:47PM +0000, Ricardo Ribalda wrote:
> > > > This patch is just shared for discussion purposes! Do not land.
> > > >
> > > > In a perfect world, after a deprecation process, we will be able to
> > > > remove allow_privacy_override and block all privacy related controls.
> > >
> > > Why add something you are only going to remove in the future?  What has
> > > changed to require this now, and will change in the future to make it
> > > not needed?
> >
> > Currently, any application with camera access can manipulate the
> > privacy LED. I believe this is a security flaw; ideally, the kernel
> > should block all such controls by default.
> >
> > However, blocking these controls immediately might be seen as a
> > regression for certain users. I added allow_privacy_override to:
> > - Prevent breaking existing workflows immediately upon a kernel update.
> > - Give users time to report why they still need manual LED control.
> >
> > The goal is to gather these use cases over the next 1–2 years. Once we
> > understand the legitimate needs, we can either implement a proper
> > specialized mechanism for them or move the toggle to a Kconfig option
> > for those who explicitly need to opt-in to the old behavior or simply
> > remove the toggle altogether.
> >
> > For the record, identified use cases so far:
> > - Old hardware with red LEDs that reflect on glasses. (Likely a dying niche).
>
> Older Logitech cameras, like the c920, have fairly large LEDs that can
> be reflective, and it's hard to cover them up aesthetically. Laurent
> wrote [1] that's why Logitech added this functionality a long time ago.
>
> You can find old Logitech software for Windows that still allows you to
> turn off the LEDs [2]. I tried it in a Win10 VM and it really works.

In the same link you will find that Logitech has removed that option
in newer version of the Logitech software and a "Official Logitech
Representative" says that:

```Please be informed that there is no way to disable the blue led
light on the webcam as that is the indicator that the webcam is active
and its mainly for security purpose.```

Other users say that
```I would not buy a webcam with an led that can be switched off and
that can watch me without any chance noticing it.```

This is definitely a requested feature by the users and it seems even
the vendor has realized that the security risk of a "stealth" camera
outweighs the benefit of turning off the LED.

>
> The c920 is a very popular camera, still available in stores today.
> Amazon says it sells 12k of them per month [3].
>
> 1:
> https://lore.kernel.org/all/20251119041907.GH10711@pendragon.ideasonboard.com/
>
> 2: https://www.reddit.com/r/logitech/comments/v641x6/comment/nr22zo7/
>
> 3: https://www.amazon.com/s?k=logitech+c920
>
> > - Using cameras as baby monitors where the LED disturbs sleep.
>
> And pet cameras and surveillance cameras don't need LEDs either.

Agree. All surveillance cameras.

All those special cases can use the allow_privacy_override parameter
until we find a good way to handle their use case.

>
> > (Arguably solvable with a piece of tape on the LED, but still a
> > reported use case).
> >
>
> Furthermore, if we filter these two Logitech XUs, we could then add the
> similar XUs of the camera modules in laptops and this could be an ever-
> growing list.

That is the goal, to have a list as comprehensive as possible.

>
> >
> Best regards,
> Gergo Koteles



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-19 11:43         ` Ricardo Ribalda
@ 2026-03-24 12:07           ` Michal Pecio
  2026-03-26 11:55             ` Ricardo Ribalda
  0 siblings, 1 reply; 15+ messages in thread
From: Michal Pecio @ 2026-03-24 12:07 UTC (permalink / raw)
  To: Ricardo Ribalda
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

On Thu, 19 Mar 2026 12:43:21 +0100, Ricardo Ribalda wrote:
> > > We can then decide if we need a specialized API for their use
> > > case or a Kconfig option, rather than leaving the current "anyone
> > > can turn off the privacy LED" status quo.  
> >
> > Why not just add the specialized API right away?  
> 
> We don't know the exact use cases yet, and I do not want to design
> an API without understanding the users for it.
> 
> At this moment, we have only identified these usecases:
> 
> - Disabling the LED to avoid reflections in glasses. (This is
>   generally a non-issue with modern hardware).
> - Baby monitors. (I would argue that physical tape is the correct
>   solution for a sleep-disturbing light).

Indeed it was a rhetorical question, I suspect this won't go anywhere
beyond the module parameter for lack of interest from users. Apparently
it's a niche thing and it already works well enough for those who care.

Kconfig could make more sense to exclude this whole "filtering" logic
for those who don't care and may not appreciate bloat, e.g. embedded.

> I rely on my LEDs. I know they are wired to the sensor power supply,
> so the LED is definitely on when the camera is in use.
> I want all users to be able to trust their LEDs like I do.

This is objectively impossible without a soldering iron, and trust in
something that's not even real is ralely a good thing.

Ultimately it's just a software controllable LED. Anyone can drive it
through USBFS. You have a point that restricting this in uvcvideo may
keep some sandboxed applications on some HW from behaving in a manner
unexpected by some users, but that's about the limit of it.

And I wish that you enjoyed the same flexibility as those Logitech
camera owners. But you wouldn't want me to try make it happen ;)

Regards,
Michal

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter
  2026-03-24 12:07           ` Michal Pecio
@ 2026-03-26 11:55             ` Ricardo Ribalda
  0 siblings, 0 replies; 15+ messages in thread
From: Ricardo Ribalda @ 2026-03-26 11:55 UTC (permalink / raw)
  To: Michal Pecio
  Cc: Laurent Pinchart, Hans de Goede, Mauro Carvalho Chehab,
	Greg Kroah-Hartman, linux-media, linux-kernel, linux-usb

Hi Michal

On Tue, 24 Mar 2026 at 13:07, Michal Pecio <michal.pecio@gmail.com> wrote:
>
> On Thu, 19 Mar 2026 12:43:21 +0100, Ricardo Ribalda wrote:
> > > > We can then decide if we need a specialized API for their use
> > > > case or a Kconfig option, rather than leaving the current "anyone
> > > > can turn off the privacy LED" status quo.
> > >
> > > Why not just add the specialized API right away?
> >
> > We don't know the exact use cases yet, and I do not want to design
> > an API without understanding the users for it.
> >
> > At this moment, we have only identified these usecases:
> >
> > - Disabling the LED to avoid reflections in glasses. (This is
> >   generally a non-issue with modern hardware).
> > - Baby monitors. (I would argue that physical tape is the correct
> >   solution for a sleep-disturbing light).
>
> Indeed it was a rhetorical question, I suspect this won't go anywhere
> beyond the module parameter for lack of interest from users. Apparently
> it's a niche thing and it already works well enough for those who care.
>
> Kconfig could make more sense to exclude this whole "filtering" logic
> for those who don't care and may not appreciate bloat, e.g. embedded.
>
> > I rely on my LEDs. I know they are wired to the sensor power supply,
> > so the LED is definitely on when the camera is in use.
> > I want all users to be able to trust their LEDs like I do.
>
> This is objectively impossible without a soldering iron, and trust in
> something that's not even real is ralely a good thing.
>
> Ultimately it's just a software controllable LED. Anyone can drive it
> through USBFS. You have a point that restricting this in uvcvideo may
> keep some sandboxed applications on some HW from behaving in a manner
> unexpected by some users, but that's about the limit of it.

Just one clarification. Not every user has permission to unbind the
uvcdriver and use ubsfs. Only priviledged users can do that.

From my point of view it is similar to the filesystem. "Anyone" can
read/write files (if they have permission) but just priviledge users
can `dd of=/dev/sda`

>
> And I wish that you enjoyed the same flexibility as those Logitech
> camera owners. But you wouldn't want me to try make it happen ;)
>
> Regards,
> Michal



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-03-26 11:55 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-16 13:34 [PATCH v3 0/4] media: uvcvideo: Map known XU controls Ricardo Ribalda
2026-03-16 13:34 ` [PATCH v3 1/4] media: uvcvideo: Import standard controls from uvcdynctrl Ricardo Ribalda
2026-03-16 13:34 ` [PATCH v3 2/4] media: uvcvideo: Announce deprecation intentions for UVCIOC_CTRL_MAP Ricardo Ribalda
2026-03-16 13:34 ` [PATCH v3 3/4] media: uvcvideo: Introduce allow_privacy_override module parameter Ricardo Ribalda
2026-03-19  0:36   ` Michal Pecio
2026-03-19  9:56     ` Ricardo Ribalda
2026-03-19 11:08       ` Michal Pecio
2026-03-19 11:43         ` Ricardo Ribalda
2026-03-24 12:07           ` Michal Pecio
2026-03-26 11:55             ` Ricardo Ribalda
2026-03-16 13:34 ` [PATCH v3 4/4] media: uvcvideo: RFC: Convert allow_privacy_override into Kconfig Ricardo Ribalda
2026-03-18 14:16   ` Greg Kroah-Hartman
2026-03-18 14:57     ` Ricardo Ribalda
2026-03-19 11:50       ` Gergo Koteles
2026-03-19 12:06         ` Ricardo Ribalda

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox