Skip to content

Read the Microsoft camera extension unit from the USB descriptor before searching #301

Description

@HardMax71

Describe your feature request

Read the USB descriptor before searching for the emitter control.

I wanted face unlock with the IR camera on my ThinkPad, expected to run configure, and ended up not needing it. The emitter sits behind a documented Microsoft control, and everything needed to drive it is readable without writing anything.

lsusb -v on the IR VideoControl interface:

bUnitID                 7
guidExtensionCode       {0f3f95dc-2632-4c4e-92c9-a04782f43bc8}
bNumControls            6
bmControls              e6 01 00 00

That GUID is MS_CAMERA_CONTROL_XU. Microsoft publishes the selector list, so the bitmap decodes. Bit N is selector N+1:

0xe6 0x01 -> bits 1,2,5,6,7,8 -> selectors 0x02 0x03 0x06 0x07 0x08 0x09
  0x02 EXPOSURE          0x03 EVCOMPENSATION    0x06 FACE_AUTHENTICATION
  0x07 CAMERA_EXTRINSICS 0x08 CAMERA_INTRINSICS 0x09 METADATA

Six bits set, six controls. The RGB interface carries the same GUID with bmControls 06 01 00 00, which is selectors 0x02, 0x03 and 0x09. Three bits, three controls. So FACE_AUTHENTICATION is on the IR side and absent from the RGB side. That is the one to go for.

Then query it. Selector 0x06 on unit 7, through UVCIOC_CTRL_QUERY on the IR node:

GET_INFO  0x03            -> GET and SET both supported
GET_LEN   9
GET_DEF   01 03 01 00 00 00 00 00 00
GET_CUR   01 03 01 00 00 00 00 00 00
GET_MIN   00 00 00 00 00 00 00 00 00
GET_MAX   01 03 03 00 00 00 00 00 00

Byte 0 is a version. Byte 1 sits at 0x03 across DEF, CUR and MAX, so it is the read-only capability mask: bit 0 alternating frame illumination, bit 1 background subtraction. Byte 2 is the mode, floor 0, ceiling 3.

CUR already reads mode 0x01 and the emitter is still dark. It wants an explicit SET_CUR. Write the nine bytes back with byte 2 at 0x01 and it lights.

Mean luma on the IR node:

before      1.08   (max 6, flat over 40 frames)
first frame 201    (max 255)
settled      58    (auto-exposure caught up)

Clean, evenly lit face. It stays on for later streams without writing again.

The light is steady rather than per-frame, whatever the mode name suggests, so the frame means show no strobing. I have not checked whether it survives a reboot or a replug. I would assume it does not.

Why would it be useful or what problem can it solve?

The search already reaches this control without knowing what it is. #299 is the same control on a Dell webcam:

unit = 4
selector = 6
control = [1, 3, 2, 0, 0, 0, 0, 0, 0]

Nine bytes, same layout, different vendor. Version 1, capability 0x03, mode 2. It was reported as constant flicker and closed as expected behaviour, on the grounds that an IR emitter has to pulse.

The mode byte might be the better explanation. Mode 2 is the background subtraction bit, which alternates lit and unlit frames on purpose so the host can subtract one from the other. Mine on mode 1 holds steady, with even and odd frame means 0.08 apart. Same control, different mode. I have one camera, so treat that as something for anyone with that Dell to try, not as a claim.

The other reason is risk. Guessing has cost people cameras. irlume #159 is a SunplusIT 174f:11b4 on a ThinkPad P14s Gen 6, where auto-discovery kept sending SET_CUR after the device began timing out. It stopped enumerating and never came back, not from a cold shutdown, not from the emergency reset hole. That laptop went in for service. #190 is an SPCA2087 on a T480, open since 2024. It ended better than it reads. The people in that thread who got their cameras back needed a full cold power-off, and one of them a Lenovo NOVO recovery boot. Plain reboots did nothing. On these laptops IR and RGB are one USB device, so a dead module takes the working webcam with it.

Reads are free. GET_ requests do not write firmware, and uvcvideo returns ENOENT for any selector the descriptor does not list, so a raw query cannot wander into unlisted ones.

Describe your implementation ideas

Searching this repo for the MSXU GUID returns nothing, so I assume the check is missing rather than considered and dropped.

irlume moved to this after #159, in PR #171. It reads the descriptor first. Then it touches only Microsoft's documented unit, only selectors that unit advertises, only when GET_INFO says the control takes a write, and only with values built from the camera's own answers. Any sign the camera has gone quiet ends it.

Roughly, before the existing search runs: walk the extension units, match the MSXU GUID, decode bmControls for selector 0x06, then GET_INFO and GET_LEN and GET_CUR. If that works, write CUR back with the mode byte set and check whether the frame brightens. Fall through to the current search only when the unit is absent or the reads fail.

I would guess most Windows Hello cameras carry the unit, since it comes with the certification, though I have only the one to go on.

For transparency: I found this while looking into my own camera and have not run this project, so I cannot point at the specific place in the code where the check would go.

Computer (or camera) model

ThinkPad P16s Gen 2, camera Chicony/SunplusIT 04f2:b7c0

Linux distribution

Debian 13, kernel 6.12


The issue was written partly with LLM in order to fix styling and formatting.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions