piaf 0.1.0

A library for reading and interpreting display capability data (EDID).
Documentation

PIAF

CI License: MPL-2.0 Rust 1.85+

A Rust library for decoding binary capability data into a clean, typed model, specialized for EDID.

PIAF reads raw EDID bytes — from a file, a kernel sysfs node, or a direct I²C read — and produces a DisplayCapabilities value with all the information a display or HDMI-adjacent application typically needs: identity, input type, supported modes, color characteristics, HDR metadata, audio capabilities, and more.

Decoding happens in two steps. parse_edid validates the raw bytes and returns a ParsedEdidRef<'_> — a zero-copy view that borrows the block structure directly from the input slice. capabilities_from_edid then runs the registered extension handlers over that structure and produces a DisplayCapabilities — the typed, stable model your application works with. Keeping these steps separate means you can inspect the raw parse result for debugging, or run multiple handler configurations over the same parsed data without re-parsing. Use parse_edid_owned to get an owned ParsedEdid that can outlive the input buffer.

use piaf::{parse_edid, capabilities_from_edid, ExtensionLibrary, ScreenSize};

let bytes = std::fs::read("/sys/class/drm/card0-HDMI-A-1/edid")?;
let library = ExtensionLibrary::with_standard_handlers();
let parsed = parse_edid(&bytes, &library)?;
let caps = capabilities_from_edid(&parsed, &library);

println!("Display: {}", caps.display_name.as_deref().unwrap_or("unknown"));
if let Some(ScreenSize::Physical { width_cm, height_cm }) = caps.screen_size {
    println!("{}×{} cm", width_cm, height_cm);
}
for mode in &caps.supported_modes {
    println!("  {}×{}@{}", mode.width, mode.height, mode.refresh_rate);
}

See examples/inspect_displays.rs for a more complete example.

flowchart LR
    bytes["&[u8]"]
    ref["ParsedEdidRef&lt;'_&gt;"]

    bytes -->|"parse_edid"| ref

    ref -->|"capabilities_from_edid\n+ ExtensionLibrary"| dc["DisplayCapabilities\nalloc / std"]
    ref -->|"capabilities_from_edid_static\n+ STANDARD_HANDLERS"| sc["StaticDisplayCapabilities&lt;N&gt;\nall tiers"]

Why PIAF

Deep CEA-861 coverage. Most EDID libraries decode the base block and stop. PIAF decodes 20+ CEA-861 data block types, including HDR static and dynamic metadata, HDMI 1.x and HDMI Forum VSDBs, colorimetry, speaker allocation, video timing blocks, and the HDMI Forum Sink Capability block. If the information is in the EDID, PIAF exposes it as typed fields rather than raw bytes.

Pluggable handlers. The extension handler system lets you register your own handler for any extension block tag — override the built-in CEA-861 handler, add support for a proprietary block, or attach typed custom data to DisplayCapabilities for downstream consumers. Base block parsing is pluggable too.

Honest diagnostics. PIAF distinguishes between hard parse errors (invalid header, checksum failure, truncated input) and non-fatal warnings (unknown extension block, malformed data block, out-of-range manufacturer ID). You decide how strict to be; nothing is silently discarded.

no_std support. The library runs on bare metal. The static extension handler pipeline — capabilities_from_edid_static with STANDARD_HANDLERS — works at all build tiers, including bare no_std without an allocator. Custom handlers implement StaticExtensionHandler using static references instead of Box — see no_std builds below.

Stable consumer model. ParsedEdidRef and ParsedEdid preserve raw bytes; DisplayCapabilities is the typed, stable output. Both implement EdidSource and work directly with the capability pipelines. Parser improvements don't change the consumer-facing API.

Extension system

Cea861Handler covers the common case. Write your own handler to support a proprietary extension block, augment CEA-861 decoding with application-specific logic, or attach typed custom data to DisplayCapabilities for downstream consumers.

Dynamic handlers (std/alloc)

Register via ExtensionLibrary. Uses Box<dyn ExtensionHandler> internally, so requires heap allocation:

use piaf::{ExtensionHandler, DisplayCapabilities, ParseWarning, ExtensionLibrary};

#[derive(Debug)]
struct MyHandler;

impl ExtensionHandler for MyHandler {
    fn process(&self, block: &[u8; 128], caps: &mut DisplayCapabilities, warnings: &mut Vec<ParseWarning>) {
        // inspect block, set fields on caps
    }
}

let mut library = ExtensionLibrary::new();
library.register(ExtensionMetadata {
    tag: 0xAB,
    display_name: String::from("My Extension"),
    handler: Some(Box::new(MyHandler)),
});

Typed data can be attached to DisplayCapabilities and retrieved by tag:

caps.set_extension_data(0xAB, MyCeaData { version: block[1] });

if let Some(data) = caps.get_extension_data::<MyCeaData>(0xAB) {
    println!("version: {}", data.version);
}

Static handlers (no-alloc)

Use StaticExtensionHandler when heap allocation is unavailable. Pass a static slice to capabilities_from_edid_static:

use piaf::{StaticExtensionHandler, ModeSink, StaticDisplayCapabilities, STANDARD_HANDLERS};

struct MyHandler;

impl StaticExtensionHandler for MyHandler {
    fn tag(&self) -> u8 { 0xAB }
    fn process(&self, block: &[u8; 128], sink: &mut dyn ModeSink) {
        // push modes via sink.push_mode(...)
    }
}

static MY_HANDLER: MyHandler = MyHandler;
static HANDLERS: &[&dyn StaticExtensionHandler] = &[STANDARD_HANDLERS[0], &MY_HANDLER];

let caps: StaticDisplayCapabilities<64> =
    piaf::capabilities_from_edid_static(&parsed, HANDLERS);

Static handlers extract modes only — audio, VSDB, colorimetry, and similar rich metadata require the dynamic pipeline.

Features

Feature Default Description
std yes Enables std-backed types and the full extension system
alloc no Enables dynamic allocation without std
serde no Derives Serialize/Deserialize on public types

no_std builds

Bare no_std (neither std nor alloc) is supported. The dynamic extension handler pipeline (ExtensionLibrary, capabilities_from_edid) requires alloc or std. The static pipeline (capabilities_from_edid_static) is available unconditionally.

In bare no_std, parse_edid returns a ParsedEdidRef<'_> that borrows extension blocks directly from the input slice — no allocator needed. Both base-block fields and extension-block modes are available through capabilities_from_edid_static at all build tiers.

parse_edid_owned returns a ParsedEdid that copies block bytes into owned storage; in bare no_std the extension block field is absent (alloc-gated), so prefer ParsedEdidRef from parse_edid when extension block access matters.

Fields in DisplayCapabilities available in all build configurations

Field Type
manufacturer Option<ManufacturerId>
manufacture_date Option<ManufactureDate>
edid_version Option<EdidVersion>
product_code Option<u16>
serial_number Option<u32>
serial_number_string Option<MonitorString>
display_name Option<MonitorString>
unspecified_text [Option<MonitorString>; 4]
white_points [Option<WhitePoint>; 2]
digital bool
color_bit_depth Option<ColorBitDepth>
video_interface Option<VideoInterface>
analog_sync_level Option<AnalogSyncLevel>
chromaticity Chromaticity
gamma Option<DisplayGamma>
display_features Option<DisplayFeatureFlags>
digital_color_encoding Option<DigitalColorEncoding>
analog_color_type Option<AnalogColorType>
screen_size Option<ScreenSize>
preferred_image_size_mm Option<(u16, u16)>
min_v_rate / max_v_rate Option<u16>
min_h_rate_khz / max_h_rate_khz Option<u16>
max_pixel_clock_mhz Option<u16>
timing_formula Option<TimingFormula>
color_management Option<ColorManagementData>
warnings [Option<EdidWarning>; 8] (first 8; use iter_warnings())

These fields are absent from DisplayCapabilities without alloc or std:

Field Reason
supported_modes Variable-length list of video modes
extension_data Type-erased handler data via Arc<dyn ExtensionData>
warnings (full) Vec<ParseWarning> in alloc builds; use iter_warnings() for portable access

For supported modes without heap allocation, use capabilities_from_edid_static instead. It returns StaticDisplayCapabilities<N>, which holds all the scalar fields above plus a fixed-capacity [Option<VideoMode>; N] array accessible via iter_modes().

Fixed-length string fields (MonitorString, ManufacturerId) use fixed-size byte array newtypes with Display and Deref<Target = str> impls, so they behave like strings in all build configurations without requiring heap allocation.

Base block decoding

Fields decoded by BaseBlockHandler:

Field Source Notes
Manufacturer ID bytes 0x080x09 Three-character PNP code; InvalidManufacturerId warning if out of range
Product code bytes 0x0A0x0B 16-bit little-endian
Serial number bytes 0x0C0x0F 32-bit little-endian
Manufacture date bytes 0x100x11 Week + year, model year, or unspecified
EDID version bytes 0x120x13 Version and revision
Input type byte 0x14 Digital/analog flag; interface type, color bit depth (digital); sync level (analog)
Screen size bytes 0x150x16 Physical dimensions in cm, or landscape/portrait aspect ratio
Chromaticity bytes 0x190x22 10-bit CIE xy coordinates for R, G, B, and white point
Display gamma byte 0x17 Encoded as (value + 100) / 100; absent if byte is 0xFF
Display features byte 0x18 DPMS states, preferred timing mode, sRGB default, continuous timings
Color encoding byte 0x18 bits 4–3 RGB/YCbCr variants for EDID 1.4+ digital; analog color type otherwise
Established timings I/II bytes 0x230x25 Bitmap of 17 legacy modes decoded as VideoMode entries
Established timings III descriptor 0xF7 Extended bitmap of 44 additional VESA modes
Standard timings bytes 0x260x35 Up to 8 resolution + refresh rate pairs decoded as VideoMode entries
Detailed timing descriptors slots 0x36, 0x48, 0x5A, 0x6C Full DTD parameters decoded as VideoMode; first non-zero image size sets preferred_image_size_mm
Monitor name descriptor 0xFC Display name string
Serial number string descriptor 0xFF Serial number as text
Unspecified text descriptor 0xFE Manufacturer-defined ASCII string
Display range limits descriptor 0xFD Min/max H and V rates, max pixel clock, GTF/CVT timing formula
Additional white points descriptor 0xFB Up to two additional white point entries with optional gamma
Color management data descriptor 0xF9 DCM polynomial coefficients for R, G, and B channels

CEA-861 coverage

Data blocks decoded by Cea861Handler:

Tag Block Notes
0x01 Audio Data Block Short Audio Descriptors (SADs)
0x02 Video Data Block VICs 1–127 (standard SVDs) and 128–255 (extended SVDs)
0x03 Vendor-Specific Data Block HDMI 1.x VSDB (OUI 0x000C03); HDMI Forum VSDB (OUI 0xC45DD8)
0x04 Speaker Allocation Data Block Three-byte channel presence bitmask
0x05 VESA Display Transfer Characteristic 8/10/12-bit packed luminance points
0x07 ext 0x00 Video Capability Data Block Quantization range and overscan flags
0x07 ext 0x01 Vendor-Specific Video Data Block IEEE OUI + opaque vendor payload (e.g. Dolby Vision)
0x07 ext 0x02 VESA Display Device Data Block Interface type, clock range, native resolution, audio, color depth
0x07 ext 0x03 VESA Video Timing Block Extension DTBs, CVT, and Standard Timing entries as VideoMode
0x07 ext 0x05 Colorimetry Data Block xvYCC, sYCC, opRGB, BT.2020 variants
0x07 ext 0x06 HDR Static Metadata Data Block EOTFs and luminance levels
0x07 ext 0x07 HDR Dynamic Metadata Data Block HDR10+, Dolby Vision application types
0x07 ext 0x0D Video Format Preference Data Block Short Video References (SVRs)
0x07 ext 0x0E YCbCr 4:2:0 Video Data Block 4:2:0-only VICs
0x07 ext 0x0F YCbCr 4:2:0 Capability Map Per-VIC 4:2:0 capability bitmap
0x07 ext 0x12 HDMI Audio Data Block Multi-stream audio flag and embedded SADs
0x07 ext 0x13 Room Configuration Data Block Speaker count and location availability
0x07 ext 0x14 Speaker Location Data Block Per-channel assignment and distance
0x07 ext 0x11 Vendor-Specific Audio Data Block IEEE OUI + opaque vendor payload
0x07 ext 0x22 DisplayID Type VII Video Timing Data Block Single 20-byte DisplayID timing descriptor decoded to VideoMode
0x07 ext 0x23 DisplayID Type VIII Video Timing Data Block VESA DMT ID codes decoded via built-in 0x01–0x58 lookup table
0x07 ext 0x2A DisplayID Type X Video Timing Data Block CVT formula-based timings; 6/7/8-byte descriptors; refresh up to 1024 Hz
0x07 ext 0x78 HDMI Forum EDID Extension Override Data Block 1-byte extension count override for HDMI 2.1 sinks
0x07 ext 0x79 HDMI Forum Sink Capability Data Block FRL rate, SCDC, Deep Color 4:2:0, ALLM, VRR range, DSC capabilities
0x07 ext 0x20 InfoFrame Data Block Short InfoFrame Descriptors with OUI for VSI

Documentation

Design and architecture notes live under doc/: