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..1a5bc623 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,21 @@ 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. 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%%:*}" + 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:--}" + done } missing_firmware() { diff --git a/tools/tests/hwreport.sh b/tools/tests/hwreport.sh index 12caed5e..94295c5e 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; 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$' @@ -490,6 +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-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-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: '