# How OpenLogi Controls UVC Camera Hardware: Zoom, Focus, Exposure, and White Balance

> Discover how OpenLogi controls UVC camera hardware zoom, focus, exposure, and white balance. Learn about mapping enums and issuing USB control requests.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-26

---

**OpenLogi controls zoom, focus, exposure, and white balance on Logitech UVC cameras by mapping high-level `CameraControl` enums to standard UVC selectors and issuing raw USB control requests through OS-specific backends.**

OpenLogi is an open-source utility that exposes professional-grade camera controls for Logitech webcams by treating them as standard **UVC (USB Video Class)** devices. The project leverages the universal UVC specification to adjust lens mechanics and image processing parameters through direct hardware register manipulation, eliminating the need for proprietary vendor SDKs.

## UVC Control Architecture

The codebase follows a layered architecture that abstracts platform differences while preserving direct hardware access. At the core, OpenLogi views every adjustable camera property as a UVC control request that targets either the **Camera Terminal** (for lens mechanics) or the **Processing Unit** (for image adjustment).

The `CameraControl` enum defined in [`crates/openlogi-camera/src/controls.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/controls.rs) serves as the platform-agnostic vocabulary. This enumeration lists every adjustable property—`Zoom`, `Focus`, `Exposure`, and `WhiteBalance`—alongside their data types and valid ranges. Complementing this, the `AutoToggle` enum tracks which controls support automatic modes, such as `AutoToggle::Focus`, `AutoToggle::Exposure`, and `AutoToggle::WhiteBalance`.

## OS-Specific Backend Implementations

While the control vocabulary remains constant across platforms, the actual USB transmission mechanism varies by operating system. Each backend translates generic `CameraControl` requests into the appropriate native API calls.

### macOS: Raw IOKit Control Transfers

On macOS, the [`crates/openlogi-camera/src/uvc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/uvc.rs) module implements hardware communication using raw IOKit `IOUSBDevRequest` structures. The `CameraControl::spec()` method maps each control to its UVC selector and target entity:

```rust
// From CameraControl::spec() in uvc.rs
Self::Zoom  => (CameraTerminal, 0x0B, Payload::U16),   // CT_ZOOM_ABSOLUTE_CONTROL
Self::Focus => (CameraTerminal, 0x06, Payload::U16),   // CT_FOCUS_ABSOLUTE_CONTROL
Self::Exposure => (CameraTerminal, 0x04, Payload::U32)   // CT_EXPOSURE_TIME_ABSOLUTE_CONTROL
Self::WhiteBalance => (Processing, 0x0A, Payload::U16)    // PU_WHITE_BALANCE_TEMPERATURE_CONTROL

```

The `set_control()` function constructs a `UVC_SET_CUR` request and writes it to the default control endpoint. Because these requests modify the camera's internal registers, changes persist across process termination and remain visible to other applications like Zoom or OBS.

### Windows: DirectShow Camera Control Interfaces

The Windows backend in [`crates/openlogi-camera/src/uvc_windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/uvc_windows.rs) wraps Microsoft's DirectShow APIs. Zoom and Focus controls map to `IAMCameraControl` property IDs (`CC_ZOOM`, `CC_FOCUS`), while white balance utilizes `IAMVideoProcAmp` (`VPA_WHITE_BALANCE`).

When setting manual values, the backend issues `Set` calls with the `FLAG_MANUAL` flag to disable automatic adjustment. The `set_auto()` function toggles the auto-flag independently without changing the underlying value, allowing users to switch between automatic and manual modes dynamically.

### Linux: V4L2 Kernel Abstraction

On Linux, [`crates/openlogi-camera/src/uvc_linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/uvc_linux.rs) communicates with the kernel's `uvcvideo` driver through Video4Linux2 (V4L2) ioctl calls. This approach delegates the low-level USB transaction details to the kernel while maintaining the same high-level API surface. The V4L2 driver forwards standard UVC GET/SET requests to the hardware, ensuring compatibility with the same selector values used on macOS.

## Camera Terminal vs. Processing Unit Controls

UVC devices organize controls into distinct functional units based on hardware capabilities.

### Lens Controls: Zoom, Focus, and Exposure

Zoom, focus, and exposure are classified as **Camera Terminal** controls because they manipulate physical lens properties. These controls use 16-bit or 32-bit unsigned integer payloads. For example, `CT_EXPOSURE_TIME_ABSOLUTE_CONTROL` (selector `0x04`) accepts a 32-bit value representing exposure time in 100-microsecond units.

When calling `set_control(unique_id, CameraControl::Zoom, 200)`, the system addresses the Camera Terminal entity with selector `0x0B` and transmits the value as a big-endian 16-bit integer.

### Image Processing: White Balance

White balance operates within the **Processing Unit** since it involves color temperature correction rather than lens mechanics. The `PU_WHITE_BALANCE_TEMPERATURE_CONTROL` (selector `0x0A`) accepts a 16-bit Kelvin value. The same `set_control()` API handles Processing Unit targets seamlessly, with the `CameraControl::spec()` method differentiating the destination entity automatically.

## Automatic Mode Management

Focus, exposure, and white balance expose separate auto-toggle endpoints defined in `AutoToggle::spec()`. The `set_auto(unique_id, toggle, boolean)` function writes to these dedicated control selectors, switching the hardware between automatic and manual modes without altering the current value.

On Windows, this corresponds to clearing or setting the `FLAG_MANUAL` bit in DirectShow property calls, while on macOS and Linux it issues distinct UVC requests to the auto-control selectors located in either the Camera Terminal or Processing Unit depending on the toggle type.

## Practical Implementation Example

The following Rust code demonstrates setting manual controls for all four parameters using the public API exported from [`crates/openlogi-camera/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/lib.rs):

```rust
use openlogi_camera::{
    CameraControl, AutoToggle, set_control, set_auto, apply_settings,
    ControlError,
};

fn configure_camera() -> Result<(), ControlError> {
    // Device unique ID from AVFoundation/DirectShow/V4L2 enumeration
    let cam_id = "0x1123000046d0893";

    // Disable auto-focus and set manual focus distance
    set_auto(cam_id, AutoToggle::Focus, false)?;
    set_control(cam_id, CameraControl::Focus, 150)?;

    // Set optical zoom to 200%
    set_control(cam_id, CameraControl::Zoom, 200)?;

    // Disable auto-exposure and set fixed exposure time (microseconds)
    set_auto(cam_id, AutoToggle::Exposure, false)?;
    set_control(cam_id, CameraControl::Exposure, 4000)?;

    // Disable auto-white-balance and force warm temperature (Kelvin)
    set_auto(cam_id, AutoToggle::WhiteBalance, false)?;
    set_control(cam_id, CameraControl::WhiteBalance, 3500)?;

    // Batch-apply multiple settings atomically
    apply_settings(
        cam_id,
        &[(AutoToggle::Focus, false), (AutoToggle::Exposure, false)],
        &[
            (CameraControl::Zoom, 250),
            (CameraControl::WhiteBalance, 3200),
        ],
    )?;
    Ok(())
}

```

The `apply_settings()` helper ensures that multiple control changes occur atomically, preventing intermediate states where some parameters are updated while others remain pending.

## Summary

- OpenLogi treats Logitech webcams as standard **UVC devices**, communicating through raw USB control requests rather than proprietary protocols.
- The **`CameraControl`** enum in [`crates/openlogi-camera/src/controls.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-camera/src/controls.rs) provides a cross-platform vocabulary for zoom, focus, exposure, and white balance.
- **Camera Terminal** controls (zoom, focus, exposure) use selectors like `0x0B`, `0x06`, and `0x04` with 16-bit or 32-bit payloads.
- **Processing Unit** controls (white balance) target selector `0x0A` with 16-bit Kelvin values.
- Platform backends in [`uvc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/uvc.rs) (macOS), [`uvc_windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/uvc_windows.rs) (Windows), and [`uvc_linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/uvc_linux.rs) (Linux) translate generic requests into IOKit, DirectShow, or V4L2 calls.
- Changes persist in hardware registers and remain active for other applications because they write to the camera's default control endpoint.

## Frequently Asked Questions

### Does OpenLogi require proprietary Logitech drivers to control camera hardware?

No. OpenLogi relies entirely on the **UVC (USB Video Class)** standard implemented in the camera firmware. The project communicates via standard USB control transfers on macOS, DirectShow on Windows, and V4L2 on Linux, requiring no vendor-specific drivers or SDKs.

### Why do camera settings persist after closing OpenLogi?

Because OpenLogi writes values to the **camera's internal registers** via the default control endpoint using `UVC_SET_CUR` requests, the hardware retains these settings independently of the host application. Any subsequent program accessing the webcam, including Zoom, Meet, or OBS, will see the modified zoom, focus, or white balance values.

### How does OpenLogi distinguish between lens controls and image processing controls?

The **`CameraControl::spec()`** method defined in the source code returns a tuple indicating the target **UVC entity**. Controls like zoom and focus return `CameraTerminal`, while white balance returns `Processing`. This distinction determines which USB request target the backend uses when constructing the control transfer.

### Can I use OpenLogi to lock exposure while keeping auto-focus enabled?

Yes. The **`set_auto()`** function controls each parameter independently through the `AutoToggle` enum. You can disable `AutoToggle::Exposure` while keeping `AutoToggle::Focus` enabled, allowing the lens to adjust dynamically while maintaining a fixed exposure time.