From ef1f777e8735e427399a6ea272af5ccf898525d4 Mon Sep 17 00:00:00 2001 From: DevomB Date: Thu, 29 Oct 2026 07:37:58 -0700 Subject: [PATCH 1/2] A built-in camera's USB device may sleep from boot The USB core keeps every new device awake until its driver allows autosuspend. For a camera that is uvcvideo's probe, and uvcvideo here binds only for a grant, so a built-in camera stayed awake from boot until its first grant, unlike on a stock system. An eudev rule sets power/control to auto for a USB device with a video control interface on a port the hub calls fixed. Webcams keep the core's default, since uvcvideo keeps a few awake that fail when they sleep. The hardware report gains a "camera usb" line per camera device, giving its port, power/control and runtime status, so the unit's own report shows whether the rule applied; hardware.md gains the row that reads it. --- build/config/udev/60-kryptik-camera.rules | 5 +++++ docs/design/laptop.md | 7 ++++++- docs/hardware.md | 1 + tools/install/kryptik-hwreport.sh | 17 +++++++++++++++-- tools/tests/hwreport.sh | 20 ++++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/build/config/udev/60-kryptik-camera.rules b/build/config/udev/60-kryptik-camera.rules index 6e282e0b..402902b1 100644 --- a/build/config/udev/60-kryptik-camera.rules +++ b/build/config/udev/60-kryptik-camera.rules @@ -1,3 +1,8 @@ # A granted camera taken away ends its grant at once, before another camera plugged in can take # its node's number: kryptikd ends it only for the granted camera's node, removed after the grant. ACTION=="remove", SUBSYSTEM=="video4linux", RUN+="/usr/bin/kryptikd camera gone %p %E{SEQNUM}" + +# The USB core keeps a device awake until its driver lets it sleep, and a camera's, uvcvideo, +# binds here only for a grant. A built-in camera may sleep from boot; a webcam keeps the core's +# default, as uvcvideo keeps awake a few webcams that fail when they sleep. +ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0e01??:*", ATTR{removable}=="fixed", TEST=="power/control", ATTR{power/control}="auto" diff --git a/docs/design/laptop.md b/docs/design/laptop.md index b478a4f6..f8fffad0 100644 --- a/docs/design/laptop.md +++ b/docs/design/laptop.md @@ -515,7 +515,12 @@ and the power key, idle and inhibitors, suspend, and runtime directories schedutil (`boot.fragment`), so its clock still follows the load. PCIe link power and NVMe power states keep the kernel's defaults, and no rule turns on USB autosuspend for input devices, which too often - misbehave with it. + misbehave with it. One does for a built-in camera + (`60-kryptik-camera.rules`), as `uvcvideo` would when it binds, which + here is only for a grant; a webcam stays awake until its first grant, as + the USB core keeps awake a device whose driver has not let it sleep. The + hardware report's `camera usb` line gives the camera's port (`fixed` is + built in) and whether it sleeps. - **Brightness.** An eudev rule (`70-kryptik-backlight.rules`) gives the session's group, `kryptik`, write access to the backlight's level, not `video`, which would hand the user the GPU's device nodes past seatd. diff --git a/docs/hardware.md b/docs/hardware.md index 9c7dac6c..2f815d7c 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -105,6 +105,7 @@ let the row's note name what did not work: | a USB-C or Thunderbolt dock: its monitor, its USB ports and its Ethernet | the monitor shows where the laptop drives USB-C itself; a Thunderbolt or USB4 dock's own USB ports and Ethernet stay dark, as 1.0 builds no Thunderbolt driver, and the row's note says which worked | | closed on its dock, then the dock unplugged | it sleeps within seconds, as a lid closed undocked does | | the state passphrase at boot, typed on the built-in keyboard | it unlocks, whether the keyboard is on PS/2 or on I2C behind Intel's LPSS or AMD's DesignWare controller; the row's note names which (`/proc/bus/input/devices`). One that types nothing there is among those status.md lists, and a plain USB keyboard is the way through | +| the listing's `camera usb` line for the built-in camera, taken before any `k` | it reads `port fixed control auto runtime suspended`; a port that reads `unknown` keeps the camera awake, and the row's note gives the line | | `k` and a zone's number in the menu, then a program there that records video | a USB camera records; one the listing's `camera` line puts behind an IPU does not, as 1.0 drives none | | a USB camera given to a zone with `k` and its number, then pulled out | the grant ends at once, and the menu's Camera line says no zone has it; plugged back in, it waits for `k` again | | Firefox in `untrusted`: Alt+p, its number and `b` | a window opens; where the listing's `shstk` line says yes, this is the only test of Firefox starting with shadow stacks on, and a failure names SHSTK in what it prints | diff --git a/tools/install/kryptik-hwreport.sh b/tools/install/kryptik-hwreport.sh index 5e26a383..09c99011 100755 --- a/tools/install/kryptik-hwreport.sh +++ b/tools/install/kryptik-hwreport.sh @@ -437,8 +437,8 @@ nhlt() { # brightness.sh chooses among; each power supply, a battery's charge and kind but never its # serial number; the sleep states the kernel offers; and a camera's parts behind Intel's image # processing unit and in ACPI: its sensors, their power controller and the Visual Sensing -# Controller between the two. A USB camera, the lid and a Bluetooth controller show under usb and -# input. +# Controller between the two; and whether a USB camera sleeps while idle. A USB camera's +# interfaces, the lid and a Bluetooth controller show under usb and input. laptop() { for b in "$SYS"/class/backlight/*; do [ -e "$b" ] && printf 'backlight %s type %s max %s\n' "${b##*/}" "$(one "$b/type")" "$(one "$b/max_brightness")" @@ -533,6 +533,19 @@ cameras() { [ "$(one "$a/status")" != 0 ] || continue printf 'camera acpi %s %s\n' "${a##*/}" "$w" done + # Each USB device with a video control interface (0e/01), once even when it holds an infrared + # camera too: whether its port is built in, and whether it sleeps while idle, which + # 60-kryptik-camera.rules lets a built-in one do before a grant loads its driver. + last="" + for i in "$SYS"/bus/usb/devices/*:*; do + [ "$(one "$i/bInterfaceClass")" = 0e ] && [ "$(one "$i/bInterfaceSubClass")" = 01 ] || continue + n="${i##*/}"; n="${n%%:*}" + [ "$n" != "$last" ] || continue + last="$n"; d="$SYS/bus/usb/devices/$n" + port="$(one "$d/removable")"; pc="$(one "$d/power/control")"; rs="$(one "$d/power/runtime_status")" + printf 'camera usb %s %s:%s port %s control %s runtime %s\n' "$n" "$(hex "$d/idVendor")" \ + "$(hex "$d/idProduct")" "${port:--}" "${pc:--}" "${rs:--}" + done } missing_firmware() { diff --git a/tools/tests/hwreport.sh b/tools/tests/hwreport.sh index 12caed5e..531078a5 100755 --- a/tools/tests/hwreport.sh +++ b/tools/tests/hwreport.sh @@ -471,6 +471,22 @@ put sys/bus/acpi/devices/INTC1009:00/status 15 put sys/bus/acpi/devices/OVTI02C1:00/status 0 # The OmniVision sensor behind AMD's image processor, as amdgpu's ISP looks for it. put sys/bus/acpi/devices/OMNI5C10:00/status 15 +# A built-in USB camera with an infrared one in it, each with its control and streaming +# interfaces, asleep as the rule lets it; and a webcam, left awake. +cam=sys/devices/pci0000:00/0000:00:14.0/usb1/1-4 +put "$cam/idVendor" 0c45; put "$cam/idProduct" 6730; put "$cam/removable" fixed +put "$cam/power/control" auto; put "$cam/power/runtime_status" suspended +ln -s "$W/$cam" "$W/sys/bus/usb/devices/1-4" +for f in 1.0:01 1.1:02 1.2:01 1.3:02; do + put "$cam/1-4:${f%:*}/bInterfaceClass" 0e; put "$cam/1-4:${f%:*}/bInterfaceSubClass" "${f#*:}" + ln -s "$W/$cam/1-4:${f%:*}" "$W/sys/bus/usb/devices/1-4:${f%:*}" +done +web=sys/devices/pci0000:00/0000:00:14.0/usb1/1-5 +put "$web/idVendor" 046d; put "$web/idProduct" 085e; put "$web/removable" removable +put "$web/power/control" on; put "$web/power/runtime_status" active +put "$web/1-5:1.0/bInterfaceClass" 0e; put "$web/1-5:1.0/bInterfaceSubClass" 01 +ln -s "$W/$web" "$W/sys/bus/usb/devices/1-5" +ln -s "$W/$web/1-5:1.0" "$W/sys/bus/usb/devices/1-5:1.0" laptop_now lap "each backlight with its kind and range" '^backlight intel_backlight type raw max 19200$' lap "the firmware's as well" '^backlight acpi_video0 type firmware max 100$' @@ -490,6 +506,10 @@ lap "its sensor, by its ACPI ID" '^camera acpi OVTI01A0:00 sensor$' lap "and AMD's image processor's sensor" '^camera acpi OMNI5C10:00 sensor$' lap "the sensor's power controller" '^camera acpi INT3472:01 sensor power$' lap "and the Visual Sensing Controller" '^camera acpi INTC1009:00 visual sensing controller$' +lap "a built-in USB camera, and that it sleeps" '^camera usb 1-4 0c45:6730 port fixed control auto runtime suspended$' +lap "a webcam, and that it is kept awake" '^camera usb 1-5 046d:085e port removable control on runtime active$' +[[ "$(grep -c '^camera usb 1-4 ' "$L")" == 1 ]] && green "a USB camera once, with its infrared one" \ + || red "the USB camera $(grep -c '^camera usb 1-4 ' "$L") times" nolap "not a sensor the firmware calls absent" 'OVTI02C1' lap "no wakeup interrupt where the kernel keeps none" '^woken by irq: -$' nolap "and no AMD IOMMU line without an IVRS table" '^amd iommu: ' From da8411b0cbece85f9ac44d2cf5056197d30ca28e Mon Sep 17 00:00:00 2001 From: DevomB Date: Thu, 29 Oct 2026 07:46:41 -0700 Subject: [PATCH 2/2] The report lists each USB camera once whatever order the glob gives A locale's collation may sort 1-11:1.0 between 1-1:1.1 and 1-1:1.2, and comparing with the last device printed then printed port 1's camera twice. The devices printed are kept instead, and the fixture adds a separate infrared camera on port 11 beside the one on port 1. --- tools/install/kryptik-hwreport.sh | 10 ++++---- tools/tests/hwreport.sh | 38 ++++++++++++++++--------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tools/install/kryptik-hwreport.sh b/tools/install/kryptik-hwreport.sh index 09c99011..1a5bc623 100755 --- a/tools/install/kryptik-hwreport.sh +++ b/tools/install/kryptik-hwreport.sh @@ -535,13 +535,15 @@ cameras() { done # Each USB device with a video control interface (0e/01), once even when it holds an infrared # camera too: whether its port is built in, and whether it sleeps while idle, which - # 60-kryptik-camera.rules lets a built-in one do before a grant loads its driver. - last="" + # 60-kryptik-camera.rules lets a built-in one do before a grant loads its driver. A locale's + # collation may sort 1-11:1.0 between 1-1:1.1 and 1-1:1.2, so every device printed is kept, + # not only the last. + seen=" " for i in "$SYS"/bus/usb/devices/*:*; do [ "$(one "$i/bInterfaceClass")" = 0e ] && [ "$(one "$i/bInterfaceSubClass")" = 01 ] || continue n="${i##*/}"; n="${n%%:*}" - [ "$n" != "$last" ] || continue - last="$n"; d="$SYS/bus/usb/devices/$n" + case "$seen" in *" $n "*) continue ;; esac + seen="$seen$n "; d="$SYS/bus/usb/devices/$n" port="$(one "$d/removable")"; pc="$(one "$d/power/control")"; rs="$(one "$d/power/runtime_status")" printf 'camera usb %s %s:%s port %s control %s runtime %s\n' "$n" "$(hex "$d/idVendor")" \ "$(hex "$d/idProduct")" "${port:--}" "${pc:--}" "${rs:--}" diff --git a/tools/tests/hwreport.sh b/tools/tests/hwreport.sh index 531078a5..94295c5e 100755 --- a/tools/tests/hwreport.sh +++ b/tools/tests/hwreport.sh @@ -472,21 +472,21 @@ put sys/bus/acpi/devices/OVTI02C1:00/status 0 # The OmniVision sensor behind AMD's image processor, as amdgpu's ISP looks for it. put sys/bus/acpi/devices/OMNI5C10:00/status 15 # A built-in USB camera with an infrared one in it, each with its control and streaming -# interfaces, asleep as the rule lets it; and a webcam, left awake. -cam=sys/devices/pci0000:00/0000:00:14.0/usb1/1-4 -put "$cam/idVendor" 0c45; put "$cam/idProduct" 6730; put "$cam/removable" fixed -put "$cam/power/control" auto; put "$cam/power/runtime_status" suspended -ln -s "$W/$cam" "$W/sys/bus/usb/devices/1-4" -for f in 1.0:01 1.1:02 1.2:01 1.3:02; do - put "$cam/1-4:${f%:*}/bInterfaceClass" 0e; put "$cam/1-4:${f%:*}/bInterfaceSubClass" "${f#*:}" - ln -s "$W/$cam/1-4:${f%:*}" "$W/sys/bus/usb/devices/1-4:${f%:*}" -done -web=sys/devices/pci0000:00/0000:00:14.0/usb1/1-5 -put "$web/idVendor" 046d; put "$web/idProduct" 085e; put "$web/removable" removable -put "$web/power/control" on; put "$web/power/runtime_status" active -put "$web/1-5:1.0/bInterfaceClass" 0e; put "$web/1-5:1.0/bInterfaceSubClass" 01 -ln -s "$W/$web" "$W/sys/bus/usb/devices/1-5" -ln -s "$W/$web/1-5:1.0" "$W/sys/bus/usb/devices/1-5:1.0" +# interfaces, asleep as the rule lets it; an infrared camera of its own on port 11, which a +# locale's collation may sort among port 1's interfaces; and a webcam, left awake. +usbdev() { # usbdev PORT VENDOR PRODUCT REMOVABLE CONTROL STATUS IF:SUBCLASS... + local d="sys/devices/pci0000:00/0000:00:14.0/usb1/$1" p="$1" f; shift + put "$d/idVendor" "$1"; put "$d/idProduct" "$2"; put "$d/removable" "$3" + put "$d/power/control" "$4"; put "$d/power/runtime_status" "$5"; shift 5 + ln -s "$W/$d" "$W/sys/bus/usb/devices/$p" + for f in "$@"; do + put "$d/$p:${f%:*}/bInterfaceClass" 0e; put "$d/$p:${f%:*}/bInterfaceSubClass" "${f#*:}" + ln -s "$W/$d/$p:${f%:*}" "$W/sys/bus/usb/devices/$p:${f%:*}" + done +} +usbdev 1-1 0c45 6730 fixed auto suspended 1.0:01 1.1:02 1.2:01 1.3:02 +usbdev 1-11 0c45 6731 fixed auto suspended 1.0:01 1.1:02 +usbdev 1-5 046d 085e removable on active 1.0:01 laptop_now lap "each backlight with its kind and range" '^backlight intel_backlight type raw max 19200$' lap "the firmware's as well" '^backlight acpi_video0 type firmware max 100$' @@ -506,10 +506,12 @@ lap "its sensor, by its ACPI ID" '^camera acpi OVTI01A0:00 sensor$' lap "and AMD's image processor's sensor" '^camera acpi OMNI5C10:00 sensor$' lap "the sensor's power controller" '^camera acpi INT3472:01 sensor power$' lap "and the Visual Sensing Controller" '^camera acpi INTC1009:00 visual sensing controller$' -lap "a built-in USB camera, and that it sleeps" '^camera usb 1-4 0c45:6730 port fixed control auto runtime suspended$' +lap "a built-in USB camera, and that it sleeps" '^camera usb 1-1 0c45:6730 port fixed control auto runtime suspended$' +lap "an infrared camera of its own" '^camera usb 1-11 0c45:6731 port fixed control auto runtime suspended$' lap "a webcam, and that it is kept awake" '^camera usb 1-5 046d:085e port removable control on runtime active$' -[[ "$(grep -c '^camera usb 1-4 ' "$L")" == 1 ]] && green "a USB camera once, with its infrared one" \ - || red "the USB camera $(grep -c '^camera usb 1-4 ' "$L") times" +[[ "$(grep -c '^camera usb 1-1 ' "$L")|$(grep -c '^camera usb 1-11 ' "$L")" == "1|1" ]] \ + && green "each USB camera once, the one with an infrared camera in it too" \ + || red "the USB cameras $(grep -c '^camera usb 1-1 ' "$L") and $(grep -c '^camera usb 1-11 ' "$L") times" nolap "not a sensor the firmware calls absent" 'OVTI02C1' lap "no wakeup interrupt where the kernel keeps none" '^woken by irq: -$' nolap "and no AMD IOMMU line without an IVRS table" '^amd iommu: '