diff --git a/README.md b/README.md index e4e7959..af1b990 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ ZAR-Zig-Agent-Runtime is the Zig runtime port of OpenClaw, with parity-first del - `src/baremetal/edid.zig`, `src/baremetal/display_output.zig`, and `src/baremetal/virtio_gpu.zig` now provide EDID-backed display capability export plus bounded render/present/flush proof for the first real controller-specific path, `virtio-gpu-pci`, including digital-input, preferred-timing, CEA, DisplayID, HDMI-vendor-data, and basic-audio capability flags when present in EDID data - `src/baremetal/display_output.zig` now derives the exported connector type from EDID capability flags instead of hard-coding the virtio-gpu path as always `virtual` - the same display-output surface now exports bounded per-output entries through `oc_display_output_entry_count` and `oc_display_output_entry`, and the broad host/tool surface now exposes `display-outputs` and `display-output ` on top of that table + - the real display surface now also supports explicit connector-targeted activation through `display-activate ` and typed `DISPLAYACTIVATE `, and launch profiles now actively select the requested connector on the real virtio-gpu path instead of only passively rejecting mismatches + - `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` now also proves explicit re-activation of the connected connector succeeds and a mismatched connector request is rejected on the live controller path - `scripts/baremetal-qemu-framebuffer-console-probe-check.ps1` now proves live MMIO banner pixels plus exported adapter metadata against the freestanding PVH artifact at `640x400`, `1024x768`, and `1280x720` - `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` now proves live `virtio-gpu-pci` EDID/controller capability export plus output-entry count/entry metadata and resource-create/attach/set-scanout/flush behavior with non-zero scanout pixel readback over QEMU with `edid=on` - real HDMI/DisplayPort connector-specific scanout paths are still future depth and are not claimed by this branch diff --git a/docs/zig-port/FS5_5_HARDWARE_DRIVERS_SYSTEMS.md b/docs/zig-port/FS5_5_HARDWARE_DRIVERS_SYSTEMS.md index fc11e52..379fe37 100644 --- a/docs/zig-port/FS5_5_HARDWARE_DRIVERS_SYSTEMS.md +++ b/docs/zig-port/FS5_5_HARDWARE_DRIVERS_SYSTEMS.md @@ -147,6 +147,7 @@ Current local source-of-truth evidence: - EDID parsing now also exports capability flags for digital input, preferred timing, CEA extension presence, DisplayID extension presence, HDMI vendor data, and basic audio when those descriptors are present - `src/baremetal/display_output.zig` provides the exported display-output ABI surface plus EDID byte export and a bounded per-output entry table - `src/baremetal/virtio_gpu.zig` probes the first real controller-specific path, `virtio-gpu-pci`, through modern virtio PCI capabilities plus `GET_DISPLAY_INFO`, `GET_EDID`, bounded multi-scanout enumeration, connector-aware scanout selection, bounded 2D resource creation, guest-backing attach, transfer-to-host, and flush + - the same path now also supports explicit connector-targeted reactivation through the runtime surface, so the selected connector is no longer only inferred/exported but can be actively reselected on the real virtio-gpu path - `src/pal/framebuffer.zig` now also exposes the display-output state and EDID byte surface through the PAL seam - host regressions now prove the framebuffer export surface updates host-backed framebuffer state, glyph pixels, supported-mode enumeration, high-resolution mode switching, per-output entry export, and preservation of the last valid mode on unsupported requests - a live bare-metal PVH/QEMU proof now passes: @@ -160,6 +161,7 @@ Current local source-of-truth evidence: - `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` - exported display-output state has `magic=display_output_magic`, `api_version=2`, `backend=virtio_gpu`, `controller=virtio_gpu`, an EDID-derived connector type, and a real EDID header over `virtio-gpu-pci,edid=on` - runtime now also reports the selected virtio-gpu PCI vendor/device, PCI location, active scanout, current mode, preferred mode, physical dimensions, manufacturer/product IDs, exported EDID byte surface, the exported capability flags derived from the EDID payload, and the bounded per-output entry export for the selected scanout + - the same proof now also validates that explicit activation of the connected connector succeeds and that an explicit mismatched connector request is rejected on the live controller path - the same proof now also validates non-zero present statistics plus non-zero scanout pixels from the guest-backed render pattern after resource-create/attach/set-scanout/flush - current real source-of-truth rendered display support now covers bounded Bochs/QEMU BGA mode-setting plus virtio-gpu present/flush over the virtual scanout path - real HDMI/DisplayPort connector-specific scanout paths are not yet implemented and are not claimed by this branch @@ -373,9 +375,9 @@ Notes: - `scripts/baremetal-qemu-rtl8139-udp-probe-check.ps1` - `scripts/baremetal-qemu-rtl8139-tcp-probe-check.ps1` - `scripts/baremetal-qemu-rtl8139-gateway-probe-check.ps1` -- `src/baremetal/package_store.zig` now persists canonical package metadata beyond the entrypoint alone, including manifest fields for `name`, `root`, `entrypoint`, `script_bytes`, `asset_root`, `asset_count`, and `asset_bytes`, and `src/baremetal/tool_exec.zig` now exposes matching `ls`, `package-info`, `package-ls`, `package-cat`, `display-info`, and `display-modes` builtins on top of the same bare-metal filesystem/package layout -- `src/baremetal_main.zig` host regressions now also prove TCP zero-window block/reopen behavior, framed multi-request command-service exchange on a single live flow, structured `EXEC` request/response behavior, bounded long-response chunking under the advertised remote window, bounded typed batch request multiplexing on one live flow, typed `PUT`/`GET`/`STAT`/`LIST` service behavior, typed `INSTALL` / `MANIFEST` runtime-layout service behavior, typed `PKGAPP` / `PKGDISPLAY` / `PKGPUT` / `PKGLS` / `PKGGET` / `PKGDELETE` package/app-manifest/package-asset/uninstall behavior, typed `APPLIST` / `APPINFO` / `APPSTATE` / `APPHISTORY` / `APPSTDOUT` / `APPSTDERR` / `APPTRUST` / `APPCONNECTOR` / `APPRUN` / `APPDELETE` app-lifecycle behavior, typed `DISPLAYINFO` / `DISPLAYMODES` / `DISPLAYSET` display-query/control behavior, typed `TRUSTPUT` / `TRUSTLIST` / `TRUSTINFO` / `TRUSTACTIVE` / `TRUSTSELECT` / `TRUSTDELETE` trust-store behavior, persisted `run-script` execution through the framed TCP service seam, and package/app install/list/info/run/delete behavior on the canonical package layout -- those proofs now cover live ARP request transmission, IPv4 frame encode/decode, UDP datagram encode/decode, TCP `SYN -> SYN-ACK -> ACK` handshake plus payload exchange, dropped-first-SYN recovery, dropped-first-payload recovery, dropped-first-FIN recovery on both close sides, bounded four-way close, bounded two-flow session isolation, zero-window block/reopen, bounded sequential payload chunking, bounded sender congestion-window growth after ACK and timeout collapse back to the initial window, framed TCP command-service exchange, bounded typed batch request multiplexing on one flow with concatenated framed responses, typed TCP `PUT` upload, direct filesystem readback of the uploaded script path, typed `INSTALL` / `MANIFEST` runtime-layout service exchange with `/boot/loader.cfg` readback, typed `PKG` / `PKGLIST` / `PKGINFO` / `PKGRUN` / `PKGAPP` / `PKGDISPLAY` / `PKGDELETE` package-service exchange, typed `APPLIST` / `APPINFO` / `APPSTATE` / `APPHISTORY` / `APPSTDOUT` / `APPSTDERR` / `APPTRUST` / `APPCONNECTOR` / `APPRUN` / `APPDELETE` app-lifecycle exchange with persisted runtime-state readback, persisted history-log readback, persisted stdout/stderr readback, and uninstall cleanup, typed `TRUSTPUT` / `TRUSTLIST` / `TRUSTINFO` / `TRUSTACTIVE` / `TRUSTSELECT` / `TRUSTDELETE` trust-store exchange, selected trust-bundle query/path readback, trust-bundle deletion, post-delete remaining-list readback, canonical package entrypoint readback, package manifest readback, package app-manifest readback, persisted package display-profile readback, package-directory listing, package output readback, live `run-package` display-mode application, explicit `DISPLAYSET` mode change/readback, and TX/RX counter advance over the freestanding PVH image +- `src/baremetal/package_store.zig` now persists canonical package metadata beyond the entrypoint alone, including manifest fields for `name`, `root`, `entrypoint`, `script_bytes`, `asset_root`, `asset_count`, and `asset_bytes`, and `src/baremetal/tool_exec.zig` now exposes matching `ls`, `package-info`, `package-ls`, `package-cat`, `display-info`, `display-outputs`, `display-output`, `display-modes`, and `display-activate` builtins on top of the same bare-metal filesystem/package layout +- `src/baremetal_main.zig` host regressions now also prove TCP zero-window block/reopen behavior, framed multi-request command-service exchange on a single live flow, structured `EXEC` request/response behavior, bounded long-response chunking under the advertised remote window, bounded typed batch request multiplexing on one live flow, typed `PUT`/`GET`/`STAT`/`LIST` service behavior, typed `INSTALL` / `MANIFEST` runtime-layout service behavior, typed `PKGAPP` / `PKGDISPLAY` / `PKGPUT` / `PKGLS` / `PKGGET` / `PKGDELETE` package/app-manifest/package-asset/uninstall behavior, typed `APPLIST` / `APPINFO` / `APPSTATE` / `APPHISTORY` / `APPSTDOUT` / `APPSTDERR` / `APPTRUST` / `APPCONNECTOR` / `APPRUN` / `APPDELETE` app-lifecycle behavior, typed `DISPLAYINFO` / `DISPLAYOUTPUTS` / `DISPLAYOUTPUT` / `DISPLAYMODES` / `DISPLAYSET` / `DISPLAYACTIVATE` display-query/control behavior, typed `TRUSTPUT` / `TRUSTLIST` / `TRUSTINFO` / `TRUSTACTIVE` / `TRUSTSELECT` / `TRUSTDELETE` trust-store behavior, persisted `run-script` execution through the framed TCP service seam, and package/app install/list/info/run/delete behavior on the canonical package layout +- those proofs now cover live ARP request transmission, IPv4 frame encode/decode, UDP datagram encode/decode, TCP `SYN -> SYN-ACK -> ACK` handshake plus payload exchange, dropped-first-SYN recovery, dropped-first-payload recovery, dropped-first-FIN recovery on both close sides, bounded four-way close, bounded two-flow session isolation, zero-window block/reopen, bounded sequential payload chunking, bounded sender congestion-window growth after ACK and timeout collapse back to the initial window, framed TCP command-service exchange, bounded typed batch request multiplexing on one flow with concatenated framed responses, typed TCP `PUT` upload, direct filesystem readback of the uploaded script path, typed `INSTALL` / `MANIFEST` runtime-layout service exchange with `/boot/loader.cfg` readback, typed `PKG` / `PKGLIST` / `PKGINFO` / `PKGRUN` / `PKGAPP` / `PKGDISPLAY` / `PKGDELETE` package-service exchange, typed `APPLIST` / `APPINFO` / `APPSTATE` / `APPHISTORY` / `APPSTDOUT` / `APPSTDERR` / `APPTRUST` / `APPCONNECTOR` / `APPRUN` / `APPDELETE` app-lifecycle exchange with persisted runtime-state readback, persisted history-log readback, persisted stdout/stderr readback, and uninstall cleanup, typed `DISPLAYINFO` / `DISPLAYOUTPUTS` / `DISPLAYOUTPUT` / `DISPLAYMODES` / `DISPLAYSET` / `DISPLAYACTIVATE` exchange including explicit connected-connector activation and mismatched-connector rejection, typed `TRUSTPUT` / `TRUSTLIST` / `TRUSTINFO` / `TRUSTACTIVE` / `TRUSTSELECT` / `TRUSTDELETE` trust-store exchange, selected trust-bundle query/path readback, trust-bundle deletion, post-delete remaining-list readback, canonical package entrypoint readback, package manifest readback, package app-manifest readback, persisted package display-profile readback, package-directory listing, package output readback, live `run-package` display-mode application, explicit `DISPLAYSET` mode change/readback, and TX/RX counter advance over the freestanding PVH image - the live package-service extension required a real probe-stack fix: `runRtl8139TcpProbe()` now uses static scratch storage, reducing the project-built bare-metal stack frame from `0x3e78` to `0x3708` bytes before the live QEMU proof would pass with package install/list/run enabled - the routed UDP proof now also covers live ARP-reply learning, ARP-cache population, gateway next-hop selection for off-subnet traffic, direct-subnet gateway bypass, and routed UDP delivery with the gateway MAC on the Ethernet frame while preserving the remote IPv4 destination - A real DHCP framing/decode slice is now also closed locally: diff --git a/docs/zig-port/PHASE_CHECKLIST.md b/docs/zig-port/PHASE_CHECKLIST.md index a67116f..4cf813c 100644 --- a/docs/zig-port/PHASE_CHECKLIST.md +++ b/docs/zig-port/PHASE_CHECKLIST.md @@ -10,6 +10,7 @@ Registry status: - release evidence now also includes `release-status.json` + `release-status.md`, which snapshot package visibility plus the latest `zig-ci` / `docs-pages` / `release-preview` / `npm-release` / `python-release` workflow state for the target tag. - `FS5.6` repo-wide license refresh is now strict-closed locally: root/package license files, release evidence, package metadata, and Linux-style SPDX headers now use `GPL-2.0-only` to match the Linux-derived RTL8139 slice. - `FS5.5` framebuffer/console strict closure is now reached locally: `src/baremetal/framebuffer_console.zig` programs a real Bochs/QEMU BGA linear-framebuffer path with bounded mode support for `640x400`, `800x600`, `1024x768`, `1280x720`, and `1280x1024`, `src/baremetal/pci.zig` discovers the selected PCI display adapter as structured metadata, exposes the framebuffer BAR, and enables decode on that function, `src/baremetal/edid.zig`, `src/baremetal/display_output.zig`, and `src/baremetal/virtio_gpu.zig` now add the first real EDID-backed controller path over `virtio-gpu-pci` including exported capability flags for digital input, preferred timing, CEA, DisplayID, HDMI-vendor-data, and basic-audio metadata when present plus EDID-derived connector inference, bounded per-output entry export, and bounded resource-create/attach/set-scanout/flush behavior, `src/pal/framebuffer.zig` exposes the surface plus supported-mode enumeration and display-output state through the PAL, host regressions in `src/baremetal/framebuffer_console.zig`, `src/baremetal/virtio_gpu.zig`, `src/baremetal_main.zig`, and `src/baremetal/display_output.zig` prove framebuffer state, display-output state, adapter metadata, supported-mode enumeration, glyph pixel updates, bounded mode switching, present counters, non-zero scanout pixels, connector inference from EDID capability flags, and the output-entry table, and the live QEMU+GDB proofs `scripts/baremetal-qemu-framebuffer-console-probe-check.ps1` and `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` now read back real MMIO banner pixels plus BGA adapter metadata and real `virtio-gpu-pci` EDID/controller capability state with non-zero scanout pixels and validated output-entry metadata over the freestanding PVH artifact; real HDMI/DisplayPort connector-specific scanout paths remain future depth and are not claimed here. +- Latest FS5.5 display connector activation slice: `src/baremetal/display_output.zig` now retargets the exported active output from bounded per-output entries, `src/baremetal/tool_exec.zig` now exposes `display-activate ` and uses connector-targeted activation in launch profiles, `src/baremetal/tool_service.zig` now exposes typed `DISPLAYACTIVATE`, host/module validation proves successful activation of the connected connector plus explicit mismatched-connector rejection, and `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` now proves the same success/rejection behavior on the live `virtio-gpu-pci` controller path. - `FS5.5` keyboard/mouse strict closure is now reached locally: `src/baremetal/ps2_input.zig` has a real x86 port-I/O backed PS/2 controller path (`0x60` / `0x64` status/data/command handling, config programming, output-buffer drain, mouse-byte packet assembly), the PAL input surface remains wired through `src/pal/input.zig`, host regressions in `src/baremetal_main.zig` assert IRQ-driven queue/payload semantics, and the live QEMU+GDB proof plus wrappers (`scripts/baremetal-qemu-ps2-input-probe-check.ps1`, `scripts/baremetal-qemu-ps2-input-baseline-probe-check.ps1`, `scripts/baremetal-qemu-ps2-keyboard-event-payload-probe-check.ps1`, `scripts/baremetal-qemu-ps2-keyboard-modifier-queue-probe-check.ps1`, `scripts/baremetal-qemu-ps2-mouse-accumulator-state-probe-check.ps1`, and `scripts/baremetal-qemu-ps2-mouse-packet-payload-probe-check.ps1`) now fail directly on the mailbox baseline, keyboard payloads, modifier/queue state, mouse accumulator state, and mouse packet payload invariants over the freestanding PVH artifact. - `FS5.5` storage/disk strict closure is now reached locally: `src/baremetal/storage_backend.zig` provides the shared backend selector, `src/baremetal/ata_pio_disk.zig` provides a real ATA PIO `IDENTIFY` / `READ` / `WRITE` / `FLUSH` path plus bounded multi-partition MBR/GPT discovery/export, first-usable-MBR and protective-MBR GPT partition mounting with logical LBA translation, `src/pal/storage.zig` plus `src/baremetal/tool_layout.zig` route through that shared backend, `src/pal/storage.zig` now also exports logical base-LBA plus bounded partition count/info/select on the mounted storage view, `src/baremetal_main.zig` exposes that same partition-aware storage seam through the `oc_storage_*` ABI exports, partition selection now invalidates stale tool-layout/filesystem state and is paired with explicit `oc_tool_layout_format` plus `oc_filesystem_format` control on the selected partition, `src/baremetal/disk_installer.zig` seeds the canonical persisted install layout (`/boot`, `/system`, `/runtime/install`, bootstrap package) on the active backend, hosted/host regressions prove backend preference, identify-backed capacity, ATA mock-device read/write/flush behavior, multi-partition MBR/GPT discovery plus explicit selection, logical base-LBA translation, installer-layout persistence, ATA-backed export reporting, direct `oc_storage_*` partition export/selection behavior, rebind-safe tool-layout/filesystem invalidation, and per-partition persistence after switching between primary and secondary MBR partitions, and the live QEMU proofs `scripts/baremetal-qemu-ata-storage-probe-check.ps1` and `scripts/baremetal-qemu-ata-gpt-installer-probe-check.ps1` now boot real MBR and protective-MBR GPT raw images and prove raw ATA block mutation + readback, secondary-partition export/selection through the exported seam, secondary-partition tool-layout formatting + payload persistence, secondary-partition filesystem formatting + superblock persistence, ATA-backed tool-layout persistence, ATA-backed filesystem persistence, GPT-backed installer layout seeding, and persisted bootstrap package execution over the freestanding PVH artifact. - `FS5.5` Ethernet-driver strict closure is now reached locally: `src/baremetal/rtl8139.zig` provides the real RTL8139 PCI-discovered bring-up, MAC readout, RX ring programming, TX slot programming, and loopback-friendly datapath validation, `src/baremetal/pci.zig` discovers the I/O BAR + IRQ line and enables I/O plus bus mastering on the selected PCI function, `src/pal/net.zig` and `src/baremetal_main.zig` expose the same raw-frame PAL/export seam, host regressions prove mock-device init/send/receive behavior, and the live QEMU proof `scripts/baremetal-qemu-rtl8139-probe-check.ps1` now proves MAC readout, TX, RX loopback, payload validation, and TX/RX counter advance over the freestanding PVH artifact. diff --git a/docs/zig-port/PORT_PLAN.md b/docs/zig-port/PORT_PLAN.md index c186712..260c291 100644 --- a/docs/zig-port/PORT_PLAN.md +++ b/docs/zig-port/PORT_PLAN.md @@ -18,6 +18,7 @@ Full-stack replacement execution reference: - glyph rendering into the hardware-backed MMIO surface - structured PCI display-adapter discovery shipped in `src/baremetal/pci.zig` and the PAL surface is exposed in `src/pal/framebuffer.zig`, with bounded mode switching plus supported-mode enumeration exported through `oc_framebuffer_set_mode`, `oc_framebuffer_supported_mode_count`, `oc_framebuffer_supported_mode_width`, and `oc_framebuffer_supported_mode_height`. - `src/baremetal/edid.zig`, `src/baremetal/display_output.zig`, and `src/baremetal/virtio_gpu.zig` now add the first real EDID-backed controller-capability path over `virtio-gpu-pci`, with exported display-output state, bounded per-output entry export, and EDID bytes routed through `src/pal/framebuffer.zig` and the bare-metal ABI. + - the same real virtio-gpu path now also supports explicit connector-targeted activation through `display-activate` and typed `DISPLAYACTIVATE`, and the live probe now proves success on the connected connector plus rejection of a mismatched connector request. - hosted/host regressions now prove framebuffer state, display-output state, output-entry metadata, adapter metadata, supported-mode enumeration, glyph pixel updates, bounded mode switching, and preservation of the last valid mode on unsupported requests. - live QEMU+GDB proof `scripts/baremetal-qemu-framebuffer-console-probe-check.ps1` reads back real MMIO banner pixels plus exported adapter metadata from the hardware-backed framebuffer BAR over the freestanding PVH artifact at `640x400`, `1024x768`, and `1280x720`. - live QEMU+GDB proof `scripts/baremetal-qemu-virtio-gpu-display-probe-check.ps1` reads back real `virtio-gpu-pci` EDID/controller capability state, including scanout geometry, physical size, manufacturer/product IDs, EDID bytes, and the exported output-entry metadata for the selected scanout. diff --git a/src/baremetal/display_output.zig b/src/baremetal/display_output.zig index e1c5dfb..b943086 100644 --- a/src/baremetal/display_output.zig +++ b/src/baremetal/display_output.zig @@ -142,6 +142,31 @@ pub fn outputEntry(index: u16) OutputEntry { return oc_display_output_entries_data[idx]; } +pub fn selectOutputConnector(connector_type: u8) bool { + var index: usize = 0; + while (index < oc_display_output_entry_count_data and index < oc_display_output_entries_data.len) : (index += 1) { + const entry = oc_display_output_entries_data[index]; + if (entry.connected == 0 or entry.connector_type != connector_type) continue; + state.connector_type = entry.connector_type; + state.connected = entry.connected; + state.edid_present = entry.edid_present; + state.active_scanout = entry.scanout_index; + state.current_width = entry.current_width; + state.current_height = entry.current_height; + state.preferred_width = entry.preferred_width; + state.preferred_height = entry.preferred_height; + state.physical_width_mm = entry.physical_width_mm; + state.physical_height_mm = entry.physical_height_mm; + state.manufacturer_id = entry.manufacturer_id; + state.product_code = entry.product_code; + state.serial_number = entry.serial_number; + state.capability_flags = entry.capability_flags; + state.edid_length = entry.edid_length; + return true; + } + return false; +} + pub fn edidByte(index: u16) u8 { const idx: usize = @intCast(index); if (idx >= state.edid_length or idx >= edid_bytes.len) return 0; @@ -431,3 +456,69 @@ test "display output state stores multiple virtio scanout entries" { try std.testing.expectEqual(@as(u8, abi.display_connector_displayport), connected.connector_type); try std.testing.expectEqual(@as(u16, 1920), connected.current_width); } + +test "display output can retarget active connector from stored entries" { + resetForTest(); + updateFromVirtioGpu(.{ + .vendor_id = 0x1AF4, + .device_id = 0x1050, + .pci_bus = 0, + .pci_device = 2, + .pci_function = 0, + .hardware_backed = true, + .connected = true, + .scanout_count = 2, + .active_scanout = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid = &.{ 0x00, 0xFF, 0xFF, 0xFF }, + .scanouts = &.{ + .{ + .connected = true, + .scanout_index = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + .{ + .connected = true, + .scanout_index = 1, + .current_width = 1920, + .current_height = 1080, + .preferred_width = 1920, + .preferred_height = 1080, + .physical_width_mm = 520, + .physical_height_mm = 320, + .manufacturer_id = 0xAAAA, + .product_code = 0xBBBB, + .serial_number = 0xCCCCDDDD, + .capability_flags = abi.display_capability_displayid_extension | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + }, + }); + + try std.testing.expect(selectOutputConnector(abi.display_connector_displayport)); + const output = statePtr(); + try std.testing.expectEqual(@as(u8, abi.display_connector_displayport), output.connector_type); + try std.testing.expectEqual(@as(u8, 1), output.active_scanout); + try std.testing.expectEqual(@as(u16, 1920), output.current_width); + try std.testing.expectEqual(@as(u16, 1080), output.current_height); + try std.testing.expect(!selectOutputConnector(abi.display_connector_embedded_displayport)); +} diff --git a/src/baremetal/tool_exec.zig b/src/baremetal/tool_exec.zig index e608e10..7e3f932 100644 --- a/src/baremetal/tool_exec.zig +++ b/src/baremetal/tool_exec.zig @@ -11,6 +11,7 @@ const display_output = @import("display_output.zig"); const framebuffer_console = @import("framebuffer_console.zig"); const vga_text_console = @import("vga_text_console.zig"); const storage_backend = @import("storage_backend.zig"); +const virtio_gpu = @import("virtio_gpu.zig"); pub const Error = filesystem.Error || trust_store.Error || app_runtime.Error || workspace_runtime.Error || std.mem.Allocator.Error || error{ MissingCommand, @@ -154,7 +155,7 @@ fn execute( if (depth > max_script_depth) return error.ScriptDepthExceeded; if (std.ascii.eqlIgnoreCase(parsed.name, "help")) { - try stdout_buffer.appendLine("OpenClaw bare-metal builtins: help, echo, cat, write-file, mkdir, stat, ls, package-info, package-verify, package-app, package-display, package-ls, package-cat, package-delete, package-release-list, package-release-info, package-release-save, package-release-activate, package-release-delete, package-release-prune, package-release-channel-list, package-release-channel-info, package-release-channel-set, package-release-channel-activate, app-list, app-info, app-state, app-history, app-stdout, app-stderr, app-trust, app-connector, app-plan-list, app-plan-info, app-plan-active, app-plan-save, app-plan-apply, app-plan-delete, app-suite-list, app-suite-info, app-suite-save, app-suite-apply, app-suite-run, app-suite-delete, app-suite-release-list, app-suite-release-info, app-suite-release-save, app-suite-release-activate, app-suite-release-delete, app-suite-release-prune, app-suite-release-channel-list, app-suite-release-channel-info, app-suite-release-channel-set, app-suite-release-channel-activate, app-delete, app-autorun-list, app-autorun-add, app-autorun-remove, app-autorun-run, workspace-plan-list, workspace-plan-info, workspace-plan-active, workspace-plan-save, workspace-plan-apply, workspace-plan-delete, workspace-plan-release-list, workspace-plan-release-info, workspace-plan-release-save, workspace-plan-release-activate, workspace-plan-release-delete, workspace-plan-release-prune, workspace-suite-list, workspace-suite-info, workspace-suite-save, workspace-suite-apply, workspace-suite-run, workspace-suite-delete, workspace-suite-release-list, workspace-suite-release-info, workspace-suite-release-save, workspace-suite-release-activate, workspace-suite-release-delete, workspace-suite-release-prune, workspace-suite-release-channel-list, workspace-suite-release-channel-info, workspace-suite-release-channel-set, workspace-suite-release-channel-activate, workspace-list, workspace-info, workspace-save, workspace-apply, workspace-run, workspace-state, workspace-history, workspace-stdout, workspace-stderr, workspace-delete, workspace-release-list, workspace-release-info, workspace-release-save, workspace-release-activate, workspace-release-delete, workspace-release-prune, workspace-release-channel-list, workspace-release-channel-info, workspace-release-channel-set, workspace-release-channel-activate, workspace-autorun-list, workspace-autorun-add, workspace-autorun-remove, workspace-autorun-run, trust-list, trust-info, trust-active, trust-select, trust-delete, runtime-snapshot, runtime-sessions, runtime-session, display-info, display-outputs, display-output, display-modes, display-set, run-script, run-package, app-run"); + try stdout_buffer.appendLine("OpenClaw bare-metal builtins: help, echo, cat, write-file, mkdir, stat, ls, package-info, package-verify, package-app, package-display, package-ls, package-cat, package-delete, package-release-list, package-release-info, package-release-save, package-release-activate, package-release-delete, package-release-prune, package-release-channel-list, package-release-channel-info, package-release-channel-set, package-release-channel-activate, app-list, app-info, app-state, app-history, app-stdout, app-stderr, app-trust, app-connector, app-plan-list, app-plan-info, app-plan-active, app-plan-save, app-plan-apply, app-plan-delete, app-suite-list, app-suite-info, app-suite-save, app-suite-apply, app-suite-run, app-suite-delete, app-suite-release-list, app-suite-release-info, app-suite-release-save, app-suite-release-activate, app-suite-release-delete, app-suite-release-prune, app-suite-release-channel-list, app-suite-release-channel-info, app-suite-release-channel-set, app-suite-release-channel-activate, app-delete, app-autorun-list, app-autorun-add, app-autorun-remove, app-autorun-run, workspace-plan-list, workspace-plan-info, workspace-plan-active, workspace-plan-save, workspace-plan-apply, workspace-plan-delete, workspace-plan-release-list, workspace-plan-release-info, workspace-plan-release-save, workspace-plan-release-activate, workspace-plan-release-delete, workspace-plan-release-prune, workspace-suite-list, workspace-suite-info, workspace-suite-save, workspace-suite-apply, workspace-suite-run, workspace-suite-delete, workspace-suite-release-list, workspace-suite-release-info, workspace-suite-release-save, workspace-suite-release-activate, workspace-suite-release-delete, workspace-suite-release-prune, workspace-suite-release-channel-list, workspace-suite-release-channel-info, workspace-suite-release-channel-set, workspace-suite-release-channel-activate, workspace-list, workspace-info, workspace-save, workspace-apply, workspace-run, workspace-state, workspace-history, workspace-stdout, workspace-stderr, workspace-delete, workspace-release-list, workspace-release-info, workspace-release-save, workspace-release-activate, workspace-release-delete, workspace-release-prune, workspace-release-channel-list, workspace-release-channel-info, workspace-release-channel-set, workspace-release-channel-activate, workspace-autorun-list, workspace-autorun-add, workspace-autorun-remove, workspace-autorun-run, trust-list, trust-info, trust-active, trust-select, trust-delete, runtime-snapshot, runtime-sessions, runtime-session, display-info, display-outputs, display-output, display-modes, display-set, display-activate, run-script, run-package, app-run"); return; } @@ -3086,6 +3087,40 @@ fn execute( return; } + if (std.ascii.eqlIgnoreCase(parsed.name, "display-activate")) { + const connector_arg = parseFirstArg(parsed.rest) catch |err| { + exit_code.* = 2; + try writeCommandError(stderr_buffer, err, "display-activate "); + return; + }; + if (connector_arg.rest.len != 0) { + exit_code.* = 2; + try stderr_buffer.appendLine("usage: display-activate "); + return; + } + const connector_type = package_store.parseConnectorType(connector_arg.arg) catch { + exit_code.* = 2; + try stderr_buffer.appendLine("usage: display-activate "); + return; + }; + activateDisplayConnector(connector_type) catch |err| { + exit_code.* = 1; + try stderr_buffer.appendFmt("display-activate failed: {s}\n", .{@errorName(err)}); + return; + }; + const output = display_output.statePtr(); + try stdout_buffer.appendFmt( + "display connector {s} active scanout={d} current={d}x{d}\n", + .{ + displayConnectorName(output.connector_type), + output.active_scanout, + output.current_width, + output.current_height, + }, + ); + return; + } + if (std.ascii.eqlIgnoreCase(parsed.name, "run-script")) { const arg = parseFirstArg(parsed.rest) catch |err| { exit_code.* = 2; @@ -3181,6 +3216,33 @@ fn ensureDisplayReadyForMode(width: u16, height: u16) error{UnsupportedMode}!voi } } +pub fn activateDisplayConnector(connector_type: u8) Error!void { + if (connector_type == abi.display_connector_none) return; + ensureDisplayReady(); + const display_state = display_output.statePtr(); + if (display_state.connector_type == connector_type and + (display_state.connected == 1 or display_state.controller != abi.display_controller_virtio_gpu)) + { + return; + } + + if (display_state.controller == abi.display_controller_virtio_gpu) { + if (display_state.hardware_backed != 0) { + _ = virtio_gpu.probeAndPresentPatternForConnector(connector_type) catch return error.DisplayConnectorMismatch; + } else if (!display_output.selectOutputConnector(connector_type)) { + return error.DisplayConnectorMismatch; + } + } else if (display_state.connector_type == connector_type) { + return; + } + + const refreshed = display_output.statePtr(); + if (refreshed.connector_type != connector_type) return error.DisplayConnectorMismatch; + if (refreshed.controller == abi.display_controller_virtio_gpu and refreshed.connected != 1) { + return error.DisplayConnectorMismatch; + } +} + fn displayControllerName(value: u8) []const u8 { return switch (value) { abi.display_controller_bochs_bga => "bochs-bga", @@ -3281,11 +3343,12 @@ fn runLaunchProfile( try stderr_buffer.appendFmt("{s} failed: {s}\n", .{ operation, @errorName(err) }); return; }; - const display_state = display_output.statePtr(); - if (profile.connector_type != abi.display_connector_none and display_state.connector_type != profile.connector_type) { - exit_code.* = 1; - try stderr_buffer.appendFmt("{s} failed: {s}\n", .{ operation, @errorName(error.DisplayConnectorMismatch) }); - return; + if (profile.connector_type != abi.display_connector_none) { + activateDisplayConnector(profile.connector_type) catch |err| { + exit_code.* = 1; + try stderr_buffer.appendFmt("{s} failed: {s}\n", .{ operation, @errorName(err) }); + return; + }; } const framebuffer_state = framebuffer_console.statePtr(); if (framebuffer_state.width != profile.display_width or framebuffer_state.height != profile.display_height) { @@ -3806,6 +3869,78 @@ test "baremetal tool exec reports current display info and supported modes" { try std.testing.expect(std.mem.indexOf(u8, updated_info.stdout, "current=800x600") != null); } +test "baremetal tool exec activates requested display connector from stored outputs" { + storage_backend.resetForTest(); + filesystem.resetForTest(); + vga_text_console.resetForTest(); + framebuffer_console.resetForTest(); + display_output.resetForTest(); + display_output.updateFromVirtioGpu(.{ + .vendor_id = 0x1AF4, + .device_id = 0x1050, + .pci_bus = 0, + .pci_device = 2, + .pci_function = 0, + .hardware_backed = false, + .connected = true, + .scanout_count = 2, + .active_scanout = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid = &.{ 0x00, 0xFF, 0xFF, 0xFF }, + .scanouts = &.{ + .{ + .connected = true, + .scanout_index = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + .{ + .connected = true, + .scanout_index = 1, + .current_width = 1920, + .current_height = 1080, + .preferred_width = 1920, + .preferred_height = 1080, + .physical_width_mm = 520, + .physical_height_mm = 320, + .manufacturer_id = 0xAAAA, + .product_code = 0xBBBB, + .serial_number = 0xCCCCDDDD, + .capability_flags = abi.display_capability_displayid_extension | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + }, + }); + + var activate_result = try runCapture(std.testing.allocator, "display-activate displayport", 256, 256); + defer activate_result.deinit(std.testing.allocator); + try std.testing.expectEqual(@as(u8, 0), activate_result.exit_code); + try std.testing.expect(std.mem.indexOf(u8, activate_result.stdout, "display connector displayport active scanout=1 current=1920x1080") != null); + + var mismatch_result = try runCapture(std.testing.allocator, "display-activate embedded-displayport", 256, 256); + defer mismatch_result.deinit(std.testing.allocator); + try std.testing.expectEqual(@as(u8, 1), mismatch_result.exit_code); + try std.testing.expect(std.mem.indexOf(u8, mismatch_result.stderr, "display-activate failed: DisplayConnectorMismatch") != null); +} + test "baremetal tool exec persists package display mode and applies it during package launch" { storage_backend.resetForTest(); filesystem.resetForTest(); diff --git a/src/baremetal/tool_service.zig b/src/baremetal/tool_service.zig index 01fb265..0b9c45d 100644 --- a/src/baremetal/tool_service.zig +++ b/src/baremetal/tool_service.zig @@ -308,6 +308,7 @@ fn handleFramedPayload( .display_output => |output_index| try handleDisplayOutputRequest(allocator, output_index, payload_limit), .display_modes => try handleDisplayModesRequest(allocator, payload_limit), .display_set => |display_mode| try handleDisplaySetRequest(allocator, display_mode.width, display_mode.height, payload_limit), + .display_activate => |connector_name| try handleDisplayActivateRequest(allocator, connector_name, payload_limit), .trust_install => |trust_request| try handleTrustInstallRequest(allocator, trust_request.path, trust_request.body, payload_limit), .trust_list => try handleTrustListRequest(allocator, payload_limit), .trust_info => |trust_name| try handleTrustInfoRequest(allocator, trust_name, payload_limit), @@ -1924,6 +1925,29 @@ fn handleDisplaySetRequest(allocator: std.mem.Allocator, width: u16, height: u16 return response; } +fn handleDisplayActivateRequest(allocator: std.mem.Allocator, connector_name: []const u8, payload_limit: usize) Error![]u8 { + const connector_type = package_store.parseConnectorType(connector_name) catch |err| { + return formatOperationError(allocator, "DISPLAYACTIVATE", err, payload_limit); + }; + tool_exec.activateDisplayConnector(connector_type) catch |err| { + return formatOperationError(allocator, "DISPLAYACTIVATE", err, payload_limit); + }; + const output = display_output.statePtr(); + const response = try std.fmt.allocPrint( + allocator, + "DISPLAYACTIVATE {s} scanout={d} current={d}x{d}\n", + .{ + displayConnectorName(output.connector_type), + output.active_scanout, + output.current_width, + output.current_height, + }, + ); + errdefer allocator.free(response); + if (response.len > payload_limit) return error.ResponseTooLarge; + return response; +} + fn handleTrustListRequest(allocator: std.mem.Allocator, payload_limit: usize) Error![]u8 { return trust_store.listBundlesAlloc(allocator, payload_limit) catch |err| { return formatOperationError(allocator, "TRUSTLIST", err, payload_limit); @@ -3330,6 +3354,75 @@ test "baremetal tool service reports display info and supported modes" { try std.testing.expect(std.mem.indexOf(u8, updated_info, "current=800x600") != null); } +test "baremetal tool service activates requested display connector" { + storage_backend.resetForTest(); + filesystem.resetForTest(); + framebuffer_console.resetForTest(); + display_output.resetForTest(); + display_output.updateFromVirtioGpu(.{ + .vendor_id = 0x1AF4, + .device_id = 0x1050, + .pci_bus = 0, + .pci_device = 2, + .pci_function = 0, + .hardware_backed = false, + .connected = true, + .scanout_count = 2, + .active_scanout = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid = &.{ 0x00, 0xFF, 0xFF, 0xFF }, + .scanouts = &.{ + .{ + .connected = true, + .scanout_index = 0, + .current_width = 1280, + .current_height = 720, + .preferred_width = 1280, + .preferred_height = 720, + .physical_width_mm = 300, + .physical_height_mm = 190, + .manufacturer_id = 0x1111, + .product_code = 0x2222, + .serial_number = 0x33334444, + .capability_flags = abi.display_capability_hdmi_vendor_data | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + .{ + .connected = true, + .scanout_index = 1, + .current_width = 1920, + .current_height = 1080, + .preferred_width = 1920, + .preferred_height = 1080, + .physical_width_mm = 520, + .physical_height_mm = 320, + .manufacturer_id = 0xAAAA, + .product_code = 0xBBBB, + .serial_number = 0xCCCCDDDD, + .capability_flags = abi.display_capability_displayid_extension | abi.display_capability_preferred_timing, + .edid_length = 128, + }, + }, + }); + + const activate_response = try handleFramedRequest(std.testing.allocator, "REQ 67 DISPLAYACTIVATE displayport", 512, 256, 512); + defer std.testing.allocator.free(activate_response); + try std.testing.expectEqualStrings("RESP 67 56\nDISPLAYACTIVATE displayport scanout=1 current=1920x1080\n", activate_response); + + const mismatch_response = try handleFramedRequest(std.testing.allocator, "REQ 68 DISPLAYACTIVATE embedded-displayport", 512, 256, 512); + defer std.testing.allocator.free(mismatch_response); + try std.testing.expectEqualStrings("RESP 68 46\nERR DISPLAYACTIVATE: DisplayConnectorMismatch\n", mismatch_response); +} + test "baremetal tool service rotates queries and deletes trust bundles" { resetPersistentStateForTest(); diff --git a/src/baremetal/tool_service/codec.zig b/src/baremetal/tool_service/codec.zig index d15163a..d4943cc 100644 --- a/src/baremetal/tool_service/codec.zig +++ b/src/baremetal/tool_service/codec.zig @@ -136,6 +136,7 @@ pub const RequestOp = enum { display_output, display_modes, display_set, + display_activate, trust_install, trust_list, trust_info, @@ -436,6 +437,7 @@ pub const FramedRequest = struct { display_output: []const u8, display_modes: void, display_set: DisplayModeRequest, + display_activate: []const u8, trust_install: PutRequest, trust_list: void, trust_info: []const u8, @@ -2723,6 +2725,21 @@ pub fn parseFramedRequestPrefix(request: []const u8) Error!ConsumedRequest { }; } + if (std.ascii.eqlIgnoreCase(op_part.token, "DISPLAYACTIVATE")) { + const connector_part = try splitFirstToken(op_part.rest); + if (connector_part.rest.len != 0) return error.InvalidFrame; + if (newline_index != null) { + return .{ + .framed = .{ .request_id = request_id, .operation = .{ .display_activate = connector_part.token } }, + .consumed_len = prefix_len + newline_index.? + 1, + }; + } + return .{ + .framed = .{ .request_id = request_id, .operation = .{ .display_activate = connector_part.token } }, + .consumed_len = request.len, + }; + } + if (std.ascii.eqlIgnoreCase(op_part.token, "TRUSTLIST")) { if (op_part.rest.len != 0) return error.InvalidFrame; if (newline_index != null) { diff --git a/src/baremetal_main.zig b/src/baremetal_main.zig index 49f5da9..bdc95c0 100644 --- a/src/baremetal_main.zig +++ b/src/baremetal_main.zig @@ -640,6 +640,8 @@ const VirtioGpuDisplayProbeError = error{ BackendMismatch, ControllerMismatch, ConnectorMismatch, + ExplicitConnectorActivateMismatch, + MissingConnectorMismatchFailure, HardwareBackedMismatch, ConnectedMismatch, EdidPresenceMismatch, @@ -10398,25 +10400,27 @@ fn virtioGpuDisplayProbeFailureCode(err: VirtioGpuDisplayProbeError) u8 { error.BackendMismatch => 0x52, error.ControllerMismatch => 0x53, error.ConnectorMismatch => 0x54, - error.HardwareBackedMismatch => 0x55, - error.ConnectedMismatch => 0x56, - error.EdidPresenceMismatch => 0x57, - error.VendorMismatch => 0x58, - error.DeviceMismatch => 0x59, - error.ActiveScanoutMismatch => 0x5A, - error.CurrentModeMismatch => 0x5B, - error.PreferredModeMismatch => 0x5C, - error.PhysicalSizeMismatch => 0x5D, - error.ManufacturerMismatch => 0x5E, - error.ProductMismatch => 0x5F, - error.SerialMismatch => 0x60, - error.EdidLengthMismatch => 0x61, - error.EdidHeaderMismatch => 0x62, - error.CapabilityFlagsMismatch => 0x63, - error.OutputEntryCountMismatch => 0x64, - error.OutputEntryMismatch => 0x65, - error.PresentStatsMismatch => 0x66, - error.RenderedPixelMismatch => 0x67, + error.ExplicitConnectorActivateMismatch => 0x55, + error.MissingConnectorMismatchFailure => 0x56, + error.HardwareBackedMismatch => 0x57, + error.ConnectedMismatch => 0x58, + error.EdidPresenceMismatch => 0x59, + error.VendorMismatch => 0x5A, + error.DeviceMismatch => 0x5B, + error.ActiveScanoutMismatch => 0x5C, + error.CurrentModeMismatch => 0x5D, + error.PreferredModeMismatch => 0x5E, + error.PhysicalSizeMismatch => 0x5F, + error.ManufacturerMismatch => 0x60, + error.ProductMismatch => 0x61, + error.SerialMismatch => 0x62, + error.EdidLengthMismatch => 0x63, + error.EdidHeaderMismatch => 0x64, + error.CapabilityFlagsMismatch => 0x65, + error.OutputEntryCountMismatch => 0x66, + error.OutputEntryMismatch => 0x67, + error.PresentStatsMismatch => 0x68, + error.RenderedPixelMismatch => 0x69, }; } @@ -10687,6 +10691,51 @@ fn runVirtioGpuDisplayProbe() VirtioGpuDisplayProbeError!void { for (edid_header, 0..) |expected, index| { if (oc_display_output_edid_byte(@intCast(index)) != expected) return error.EdidHeaderMismatch; } + + const explicit = virtio_gpu.probeAndPresentPatternForConnector(expected_connector) catch |err| switch (err) { + error.UnsupportedPlatform => return error.UnsupportedPlatform, + error.DeviceNotFound => return error.DeviceNotFound, + error.MissingCapabilities => return error.MissingCapabilities, + error.MissingVersion1 => return error.MissingVersion1, + error.MissingEdidFeature => return error.MissingEdidFeature, + error.FeaturesRejected => return error.FeaturesRejected, + error.QueueUnavailable => return error.QueueUnavailable, + error.QueueTooSmall => return error.QueueTooSmall, + error.RequestTimedOut => return error.RequestTimedOut, + error.InvalidDisplayInfoResponse => return error.InvalidDisplayInfoResponse, + error.InvalidEdidResponse => return error.InvalidEdidResponse, + error.NoConnectedScanout => return error.ExplicitConnectorActivateMismatch, + error.InvalidEdid => return error.InvalidEdid, + error.InvalidControlResponse => return error.InvalidControlResponse, + error.FramebufferTooLarge => return error.FramebufferTooLarge, + }; + if (explicit.active_scanout != result.active_scanout or explicit.capability_flags != result.capability_flags) { + return error.ExplicitConnectorActivateMismatch; + } + + const wrong_connector: u8 = switch (expected_connector) { + abi.display_connector_hdmi => abi.display_connector_displayport, + abi.display_connector_displayport, abi.display_connector_embedded_displayport => abi.display_connector_hdmi, + else => abi.display_connector_hdmi, + }; + _ = virtio_gpu.probeAndPresentPatternForConnector(wrong_connector) catch |err| switch (err) { + error.NoConnectedScanout => return, + error.UnsupportedPlatform => return error.UnsupportedPlatform, + error.DeviceNotFound => return error.DeviceNotFound, + error.MissingCapabilities => return error.MissingCapabilities, + error.MissingVersion1 => return error.MissingVersion1, + error.MissingEdidFeature => return error.MissingEdidFeature, + error.FeaturesRejected => return error.FeaturesRejected, + error.QueueUnavailable => return error.QueueUnavailable, + error.QueueTooSmall => return error.QueueTooSmall, + error.RequestTimedOut => return error.RequestTimedOut, + error.InvalidDisplayInfoResponse => return error.InvalidDisplayInfoResponse, + error.InvalidEdidResponse => return error.InvalidEdidResponse, + error.InvalidEdid => return error.InvalidEdid, + error.InvalidControlResponse => return error.InvalidControlResponse, + error.FramebufferTooLarge => return error.FramebufferTooLarge, + }; + return error.MissingConnectorMismatchFailure; } fn ataStorageProbeFailureCode(err: AtaStorageProbeError) u8 {