feat: VirtIO GPU driver, libdrm DRM ioctls, KWin/KF6 build fixes, display stack additions
- Add full VirtIO GPU driver with command submission, resource management, VirtQueue implementation, and transport layer; includes diagnostic probes for resource_create_2d ERR_INVALID_RESOURCE_ID investigation - Expand libdrm Redox support with DRM ioctl wrappers (ADDFB, RMFB, CREATE_DUMB, MAP_DUMB, DESTROY_DUMB, GET_RESOURCES, GET_CONNECTOR, GET_CRTC, SET_CRTC, MODE_OBJ_GET_PROPERTIES, etc.) and xf86drm_redox.h - Add redox-drm scheme handlers for VirtIO GPU-specific DRM ioctls (VIRTGPU_RESOURCE_CREATE, VIRTGPU_MAP, VIRTGPU_WAIT, VIRTGPU_INFO, etc.) - Add display stack recipes: freetype2, lcms2, libdisplay-info, libepoxy, libxcvt - Fix KWin build (recipe.toml expanded, kf6-ksvg added) - Fix KF6 CMakeLists for cross-compilation (attica, kcmutils, kcolorscheme, kcompletion, kconfigwidgets, kdeclarative, kiconthemes, kitemmodels, kitemviews, kjobwidgets, ktextwidgets, kwayland, kxmlgui, kpty, solid) - Add Qt6 futex support patch - Add relibc patches: P3 strtold, P3 ld-so search path, P5 DRM ioctl removal - Add base P4 pcid config scheme patch - Update driver-manager hotplug/config, PCI config in redox-driver-sys - Update greeter compositor and KDE session scripts - Update AGENTS.md with zero-tolerance stubs policy and project knowledge
This commit is contained in:
@@ -196,6 +196,91 @@ make all
|
|||||||
- **Syscall ABI**: Unstable intentionally. Stability via `libredox` and `relibc`
|
- **Syscall ABI**: Unstable intentionally. Stability via `libredox` and `relibc`
|
||||||
- **Drivers**: ALL userspace daemons via scheme system. No kernel-space drivers (except serio)
|
- **Drivers**: ALL userspace daemons via scheme system. No kernel-space drivers (except serio)
|
||||||
|
|
||||||
|
## SYSTEM-CRITICAL INFRASTRUCTURE MUST BE RUST
|
||||||
|
|
||||||
|
All Red Bear OS system-critical infrastructure **must** be written in Rust. C and C++ are
|
||||||
|
acceptable only for ported upstream applications (KDE Plasma, Qt, games, third-party tools)
|
||||||
|
where the original source is not Rust.
|
||||||
|
|
||||||
|
### What counts as system-critical
|
||||||
|
|
||||||
|
| Layer | Component | Language | Status |
|
||||||
|
|-------|-----------|----------|--------|
|
||||||
|
| Kernel | microkernel | Rust | ✅ |
|
||||||
|
| C library | relibc | Rust | ✅ |
|
||||||
|
| Init | service manager | Rust | ✅ |
|
||||||
|
| Filesystems | redoxfs, ext4d, fatd | Rust | ✅ |
|
||||||
|
| Driver infrastructure | redox-driver-sys, linux-kpi headers | Rust + C headers | ✅ |
|
||||||
|
| Display/compositor | Wayland compositor | Rust | required |
|
||||||
|
| Session/auth | redbear-sessiond, redbear-authd | Rust | ✅ |
|
||||||
|
| D-Bus broker | session/system bus | Rust | ✅ |
|
||||||
|
| Network stack | TCP/IP, Wi-Fi control plane | Rust | required |
|
||||||
|
| Bluetooth stack | host/controller path | Rust | required |
|
||||||
|
| USB stack | controller drivers, hub driver | Rust | required |
|
||||||
|
| Input stack | evdev, libinput adapter | Rust | required |
|
||||||
|
| Firmware loading | scheme:firmware daemon | Rust | ✅ |
|
||||||
|
| Core utilities | shell, fileutils, process tools | Rust | required |
|
||||||
|
| Bootloader | UEFI bootloader | Rust | ✅ |
|
||||||
|
| Installer | redox_installer | Rust | ✅ |
|
||||||
|
| Build tooling | cookbook, repo binary | Rust | ✅ |
|
||||||
|
|
||||||
|
### What does NOT need to be Rust
|
||||||
|
|
||||||
|
- **Ported desktop applications**: KDE Plasma, Qt apps, KDE Frameworks — these are upstream
|
||||||
|
C++ codebases and remain C++. The boundary is at the platform adapter layer: anything Red Bear
|
||||||
|
writes to integrate them ( Wayland protocol bridges, D-Bus service implementations, platform
|
||||||
|
plugins) must be Rust even if the upstream consumer is C++.
|
||||||
|
- **Ported libraries**: mesa, wayland, libxkbcommon, libinput, fontconfig, etc. — upstream C.
|
||||||
|
- **Games and end-user applications**: upstream code in any language.
|
||||||
|
|
||||||
|
### Decision rule
|
||||||
|
|
||||||
|
When writing new code for Red Bear OS, or when choosing between writing new code vs porting
|
||||||
|
existing code, the rule is:
|
||||||
|
|
||||||
|
> If the component runs below the application layer — kernel, libc, drivers, filesystems,
|
||||||
|
> compositor, session management, networking, input, USB, Bluetooth, core utilities —
|
||||||
|
> it must be written in Rust.
|
||||||
|
>
|
||||||
|
> If the component is an application or library that users would recognize as a separate
|
||||||
|
> upstream project (KDE, Qt, Firefox, etc.), it may remain in its upstream language.
|
||||||
|
>
|
||||||
|
> The integration layer between Rust infrastructure and upstream C/C++ code must be Rust.
|
||||||
|
> Platform adapters, D-Bus service implementations, Wayland protocol bridges, and plugin
|
||||||
|
> shims are infrastructure, not applications.
|
||||||
|
|
||||||
|
### Enforcement
|
||||||
|
|
||||||
|
- New recipes under `local/recipes/` for system-critical components must use `template = "cargo"`.
|
||||||
|
- C/C++ build templates (`cmake`, `meson`, `make`, `configure`) are only for ported upstream
|
||||||
|
packages and their direct dependencies.
|
||||||
|
- If a ported C/C++ package needs a companion daemon, helper, or bridge, that companion must
|
||||||
|
be a separate Rust recipe — not embedded C in the ported package.
|
||||||
|
|
||||||
|
### Conflicting implementations: always prefer Rust
|
||||||
|
|
||||||
|
When both a Rust implementation and a C/C++ implementation exist for the same functionality,
|
||||||
|
Red Bear OS **always** prefers the Rust implementation. This applies even when the C version is
|
||||||
|
from upstream Redox or appears more complete.
|
||||||
|
|
||||||
|
The rationale: Rust provides memory safety, type safety, and panic-based error recovery at the
|
||||||
|
language level. For an OS with no ASLR, no stack canaries, and a minimal kernel, the language
|
||||||
|
itself is the primary defense boundary. A C implementation of equivalent functionality is always
|
||||||
|
a strictly weaker choice.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
| Situation | Correct choice |
|
||||||
|
|-----------|---------------|
|
||||||
|
| relibc (Rust) vs newlib/glibc (C) | relibc — always |
|
||||||
|
| redoxfs (Rust) vs an imported C filesystem driver | redoxfs — always |
|
||||||
|
| redbear-sessiond (Rust) vs dbus-daemon (C) | redbear-sessiond — always |
|
||||||
|
| A Rust crate for a protocol vs the reference C library | Rust crate — always |
|
||||||
|
| Upstream Redox provides a Rust driver; we also have a C port | Rust driver — always |
|
||||||
|
|
||||||
|
If a Rust implementation is less feature-complete than the C alternative, the correct response
|
||||||
|
is to improve the Rust implementation — not to fall back to C.
|
||||||
|
|
||||||
## INSTALLER FILE LAYERING
|
## INSTALLER FILE LAYERING
|
||||||
|
|
||||||
The installer creates filesystem images in four layers. Understanding this ordering is critical
|
The installer creates filesystem images in four layers. Understanding this ordering is critical
|
||||||
@@ -253,6 +338,66 @@ See `local/docs/BUILD-SYSTEM-HARDENING-PLAN.md` for the full plan.
|
|||||||
- **DO NOT** remove patches from `recipe.toml` to fix build failures — rebase the patch instead (see `local/docs/PATCH-GOVERNANCE.md`)
|
- **DO NOT** remove patches from `recipe.toml` to fix build failures — rebase the patch instead (see `local/docs/PATCH-GOVERNANCE.md`)
|
||||||
- **DO NOT** remove BINS entries to fix build failures — fix the source or use EXISTING_BINS filtering
|
- **DO NOT** remove BINS entries to fix build failures — fix the source or use EXISTING_BINS filtering
|
||||||
|
|
||||||
|
## ZERO TOLERANCE FOR STUBS
|
||||||
|
|
||||||
|
**Red Bear OS has zero tolerance for stubs, placeholders, fake implementations, skeleton code,
|
||||||
|
unimplemented functions, or any other "make it compile" shortcut.**
|
||||||
|
|
||||||
|
### What counts as a stub
|
||||||
|
|
||||||
|
Any of the following:
|
||||||
|
|
||||||
|
- A function or method that returns a hardcoded value instead of computing the real result
|
||||||
|
- A trait implementation where methods log "not implemented" or return empty/default values
|
||||||
|
- A struct that stores fields but never uses them for real work
|
||||||
|
- A file that exists to satisfy an import but contains no real logic
|
||||||
|
- A `// TODO: implement` comment followed by a dummy return
|
||||||
|
- A driver that creates synthetic data (fake EDID, hardcoded modes) instead of querying the hardware
|
||||||
|
- A `todo!()` or `unimplemented!()` macro anywhere in non-test code
|
||||||
|
|
||||||
|
### The rule
|
||||||
|
|
||||||
|
> **As soon as a stub is detected, it must be replaced with real, sophisticated, comprehensive code
|
||||||
|
> in the same work session.** No exceptions. No "I'll come back to it." No "this is good enough for now."
|
||||||
|
|
||||||
|
A stub found in the tree is a **bug** — not a precedent to follow, not a placeholder to defer,
|
||||||
|
not an acceptable intermediate state. Every stub is technical debt that compounds daily.
|
||||||
|
|
||||||
|
### Required response to a stub
|
||||||
|
|
||||||
|
When you encounter a stub:
|
||||||
|
|
||||||
|
1. **Stop what you are doing.** The stub takes priority.
|
||||||
|
2. **Understand the interface.** Read the trait, API contract, or specification the stub is supposed to implement.
|
||||||
|
3. **Implement it comprehensively.** Real logic, real hardware communication, real error handling, real edge cases.
|
||||||
|
4. **Verify it works.** Run `cargo check`, tests, or build verification.
|
||||||
|
|
||||||
|
| Stub pattern | Required fix |
|
||||||
|
|---|---|
|
||||||
|
| `fn detect_connectors() -> Vec<ConnectorInfo> { vec![] }` | Implement real hardware enumeration with proper protocol |
|
||||||
|
| `fn handle_irq() -> Ok(None)` | Implement real interrupt handling with event dispatch |
|
||||||
|
| `synthetic_edid()` when hardware can provide real EDID | Query the device via the proper protocol |
|
||||||
|
| Hardcoded mode "1280x720" | Query the display hardware for actual supported modes |
|
||||||
|
| `_firmware: HashMap<String, Vec<u8>>` (unused parameter) | Use the firmware data in device initialization |
|
||||||
|
| `Ok(self.vblank_count.fetch_add(1, Ordering::SeqCst))` in page_flip | Submit real buffer to hardware and wait for display |
|
||||||
|
| `todo!()` / `unimplemented!()` | Replace with full implementation |
|
||||||
|
|
||||||
|
### Why this matters
|
||||||
|
|
||||||
|
Stubs are worse than missing code because they:
|
||||||
|
|
||||||
|
- **Hide missing functionality** — the system appears to work but silently does nothing
|
||||||
|
- **Block real testing** — you can't verify behavior against hardware when the code doesn't talk to hardware
|
||||||
|
- **Create false confidence** — "it compiles" becomes a substitute for "it works"
|
||||||
|
- **Compound over time** — one stub leads to another as callers assume the interface is real
|
||||||
|
- **Waste debugging time** — hours spent tracing why something "doesn't display" when the driver never sent a command
|
||||||
|
|
||||||
|
### Enforcement
|
||||||
|
|
||||||
|
- Code reviews must reject any PR containing stubs
|
||||||
|
- Any agent or developer that introduces a stub must replace it before the session ends
|
||||||
|
- If a stub cannot be replaced (missing specification, blocked dependency), document it as a known gap in `local/docs/` — but never leave it in the code as a stub. Remove the code path entirely and add a clear error message instead.
|
||||||
|
|
||||||
## LINUX REFERENCE SOURCE POLICY
|
## LINUX REFERENCE SOURCE POLICY
|
||||||
|
|
||||||
`local/reference/linux-7.0/` (or later) contains a full Linux kernel source tree for
|
`local/reference/linux-7.0/` (or later) contains a full Linux kernel source tree for
|
||||||
|
|||||||
+31
-25
@@ -47,14 +47,21 @@ numad = {}
|
|||||||
redox-drm = {}
|
redox-drm = {}
|
||||||
mesa = {}
|
mesa = {}
|
||||||
libdrm = {}
|
libdrm = {}
|
||||||
|
libepoxy = {}
|
||||||
|
libdisplay-info = {}
|
||||||
|
libxcvt = {}
|
||||||
|
lcms2 = {}
|
||||||
|
freetype2 = {}
|
||||||
|
fontconfig = {}
|
||||||
|
|
||||||
libwayland = {}
|
libwayland = {}
|
||||||
wayland-protocols = {}
|
wayland-protocols = {}
|
||||||
redbear-compositor = {}
|
plasma-wayland-protocols = {}
|
||||||
|
redbear-compositor = "ignore" # replaced by kwin
|
||||||
|
|
||||||
# Keyboard/input
|
# Keyboard/input
|
||||||
# libxkbcommon = {} # build needed
|
libxkbcommon = {}
|
||||||
# xkeyboard-config = {} # build needed
|
xkeyboard-config = {}
|
||||||
libevdev = {}
|
libevdev = {}
|
||||||
libinput = {}
|
libinput = {}
|
||||||
redbear-keymapd = {}
|
redbear-keymapd = {}
|
||||||
@@ -93,7 +100,7 @@ kf6-kded6 = {}
|
|||||||
kf6-kguiaddons = {}
|
kf6-kguiaddons = {}
|
||||||
kf6-ki18n = {}
|
kf6-ki18n = {}
|
||||||
kf6-kiconthemes = {}
|
kf6-kiconthemes = {}
|
||||||
kf6-kidletime = "ignore"
|
kf6-kidletime = {}
|
||||||
kf6-kitemmodels = {}
|
kf6-kitemmodels = {}
|
||||||
kf6-kitemviews = {}
|
kf6-kitemviews = {}
|
||||||
kf6-kjobwidgets = {}
|
kf6-kjobwidgets = {}
|
||||||
@@ -101,12 +108,14 @@ kf6-knotifications = {}
|
|||||||
kf6-kpackage = {}
|
kf6-kpackage = {}
|
||||||
kf6-kservice = {}
|
kf6-kservice = {}
|
||||||
kf6-ktextwidgets = {}
|
kf6-ktextwidgets = {}
|
||||||
kf6-kwayland = "ignore"
|
kf6-kwayland = {}
|
||||||
kf6-kwidgetsaddons = {}
|
kf6-kwidgetsaddons = {}
|
||||||
|
kf6-kwindowsystem = {}
|
||||||
kf6-kxmlgui = {}
|
kf6-kxmlgui = {}
|
||||||
kf6-prison = {}
|
kf6-prison = {}
|
||||||
kf6-solid = {}
|
kf6-solid = {}
|
||||||
kf6-sonnet = {}
|
kf6-sonnet = {}
|
||||||
|
kf6-ksvg = {}
|
||||||
kf6-knewstuff = {}
|
kf6-knewstuff = {}
|
||||||
kf6-kwallet = {}
|
kf6-kwallet = {}
|
||||||
kf6-kglobalaccel = {}
|
kf6-kglobalaccel = {}
|
||||||
@@ -186,7 +195,7 @@ name = "redox-drm"
|
|||||||
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
||||||
priority = 60
|
priority = 60
|
||||||
command = ["/usr/bin/redox-drm"]
|
command = ["/usr/bin/redox-drm"]
|
||||||
depends_on = ["pci", "firmware", "iommu"]
|
depends_on = ["pci"]
|
||||||
|
|
||||||
[[driver.match]]
|
[[driver.match]]
|
||||||
class = 0x03
|
class = 0x03
|
||||||
@@ -246,6 +255,21 @@ cmd = "/usr/bin/iommu"
|
|||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
path = "/etc/init.d/14_redox-drm.service"
|
||||||
|
data = """
|
||||||
|
[unit]
|
||||||
|
description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
||||||
|
requires_weak = [
|
||||||
|
"05_boot-essential.target",
|
||||||
|
]
|
||||||
|
|
||||||
|
[service]
|
||||||
|
cmd = "/usr/bin/sh"
|
||||||
|
args = ["-c", "if ! head -c 1 /scheme/drm/card0 >/dev/null 2>&1; then exec /usr/bin/redox-drm; fi; echo 'scheme:drm already registered, skipping'"]
|
||||||
|
type = "oneshot_async"
|
||||||
|
"""
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/etc/init.d/12_dbus.service"
|
path = "/etc/init.d/12_dbus.service"
|
||||||
data = """
|
data = """
|
||||||
@@ -395,25 +419,6 @@ envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/pl
|
|||||||
type = "oneshot_async"
|
type = "oneshot_async"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[[files]]
|
|
||||||
path = "/etc/init.d/20_display.service"
|
|
||||||
data = """
|
|
||||||
[unit]
|
|
||||||
description = "KDE session assembly helper"
|
|
||||||
requires_weak = [
|
|
||||||
"12_dbus.service",
|
|
||||||
"13_redbear-sessiond.service",
|
|
||||||
"13_seatd.service",
|
|
||||||
"19_redbear-authd.service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[service]
|
|
||||||
cmd = "/usr/bin/redbear-session-launch"
|
|
||||||
args = ["--username", "root", "--mode", "session", "--session", "kde-wayland", "--vt", "4", "--runtime-dir", "/tmp/run/redbear-display-session", "--wayland-display", "wayland-display"]
|
|
||||||
envs = { QT_PLUGIN_PATH = "/usr/plugins", QT_QPA_PLATFORM_PLUGIN_PATH = "/usr/plugins/platforms", QML2_IMPORT_PATH = "/usr/qml", XCURSOR_THEME = "Pop", XKB_CONFIG_ROOT = "/usr/share/X11/xkb", REDBEAR_KDE_SESSION_BACKEND = "virtual", REDBEAR_KDE_SESSION_STATE_DIR = "/run/redbear-display-session" }
|
|
||||||
type = "oneshot_async"
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
path = "/etc/init.d/20_greeter.service"
|
path = "/etc/init.d/20_greeter.service"
|
||||||
data = """
|
data = """
|
||||||
@@ -421,6 +426,7 @@ data = """
|
|||||||
description = "Red Bear greeter service"
|
description = "Red Bear greeter service"
|
||||||
requires_weak = [
|
requires_weak = [
|
||||||
"00_driver-manager.service",
|
"00_driver-manager.service",
|
||||||
|
"14_redox-drm.service",
|
||||||
"12_dbus.service",
|
"12_dbus.service",
|
||||||
"13_redbear-sessiond.service",
|
"13_redbear-sessiond.service",
|
||||||
"13_seatd.service",
|
"13_seatd.service",
|
||||||
|
|||||||
@@ -15,14 +15,25 @@ description = "DRM/KMS display driver (AMD + Intel + VirtIO)"
|
|||||||
priority = 60
|
priority = 60
|
||||||
command = ["/usr/bin/redox-drm"]
|
command = ["/usr/bin/redox-drm"]
|
||||||
|
|
||||||
|
# Only match known GPU vendors. Class 0x03 alone catches QEMU VGA
|
||||||
|
# (vendor 0x1234) which redox-drm rejects with a fatal error.
|
||||||
[[driver.match]]
|
[[driver.match]]
|
||||||
|
vendor = 0x1002
|
||||||
|
class = 0x03
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x8086
|
||||||
|
class = 0x03
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0x1AF4
|
||||||
class = 0x03
|
class = 0x03
|
||||||
|
|
||||||
[[driver]]
|
[[driver]]
|
||||||
name = "virtio-gpud"
|
name = "redox-drm-virtio"
|
||||||
description = "VirtIO GPU driver"
|
description = "VirtIO GPU DRM/KMS driver"
|
||||||
priority = 60
|
priority = 61
|
||||||
command = ["/usr/lib/drivers/virtio-gpud"]
|
command = ["/usr/bin/redox-drm"]
|
||||||
|
|
||||||
[[driver.match]]
|
[[driver.match]]
|
||||||
vendor = 0x1AF4
|
vendor = 0x1AF4
|
||||||
|
|||||||
@@ -6,18 +6,26 @@ description = "USB CDC ACM serial driver"
|
|||||||
priority = 70
|
priority = 70
|
||||||
command = ["/usr/bin/redbear-acmd"]
|
command = ["/usr/bin/redbear-acmd"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0xFFFF
|
||||||
|
device = 0xFFFF
|
||||||
|
|
||||||
[[driver]]
|
[[driver]]
|
||||||
name = "redbear-ecmd"
|
name = "redbear-ecmd"
|
||||||
description = "USB CDC ECM/NCM ethernet driver"
|
description = "USB CDC ECM/NCM ethernet driver"
|
||||||
priority = 70
|
priority = 70
|
||||||
command = ["/usr/bin/redbear-ecmd"]
|
command = ["/usr/bin/redbear-ecmd"]
|
||||||
|
|
||||||
|
[[driver.match]]
|
||||||
|
vendor = 0xFFFF
|
||||||
|
device = 0xFFFF
|
||||||
|
|
||||||
[[driver]]
|
[[driver]]
|
||||||
name = "redbear-usbaudiod"
|
name = "redbear-usbaudiod"
|
||||||
description = "USB Audio Class driver"
|
description = "USB Audio Class driver"
|
||||||
priority = 70
|
priority = 70
|
||||||
command = ["/usr/bin/redbear-usbaudiod"]
|
command = ["/usr/bin/redbear-usbaudiod"]
|
||||||
|
|
||||||
# USB class drivers are spawned by the USB host controller (xhcid/ehcid)
|
[[driver.match]]
|
||||||
# when matching USB devices are detected, not by PCI bus scanning.
|
vendor = 0xFFFF
|
||||||
# Match entries below use USB interface class codes for host-controller-side matching.
|
device = 0xFFFF
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
diff --git a/drivers/pcid/src/cfg_access/fallback.rs b/drivers/pcid/src/cfg_access/fallback.rs
|
||||||
|
--- a/drivers/pcid/src/cfg_access/fallback.rs
|
||||||
|
+++ b/drivers/pcid/src/cfg_access/fallback.rs
|
||||||
|
@@ -61,8 +61,11 @@
|
||||||
|
|
||||||
|
Self::set_iopl();
|
||||||
|
|
||||||
|
- let offset =
|
||||||
|
- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||||
|
+ let Ok(offset) = u8::try_from(offset) else {
|
||||||
|
+ // Offset exceeds 256-byte PCI 3.0 config space — return all-ones
|
||||||
|
+ // (standard response for non-existent config space, matching Linux behavior).
|
||||||
|
+ return 0xFFFF_FFFF;
|
||||||
|
+ };
|
||||||
|
let address = Self::address(address, offset);
|
||||||
|
|
||||||
|
Pio::<u32>::new(0xCF8).write(address);
|
||||||
|
@@ -74,8 +77,9 @@
|
||||||
|
|
||||||
|
Self::set_iopl();
|
||||||
|
|
||||||
|
- let offset =
|
||||||
|
- u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space");
|
||||||
|
+ let Ok(offset) = u8::try_from(offset) else {
|
||||||
|
+ return;
|
||||||
|
+ };
|
||||||
|
let address = Self::address(address, offset);
|
||||||
|
|
||||||
|
Pio::<u32>::new(0xCF8).write(address);
|
||||||
|
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||||
|
--- a/drivers/pcid/src/scheme.rs
|
||||||
|
+++ b/drivers/pcid/src/scheme.rs
|
||||||
|
@@ -22,6 +22,7 @@
|
||||||
|
Access,
|
||||||
|
Device,
|
||||||
|
Channel { addr: PciAddress, st: ChannelState },
|
||||||
|
+ Config { addr: PciAddress },
|
||||||
|
SchemeRoot,
|
||||||
|
}
|
||||||
|
struct HandleWrapper {
|
||||||
|
@@ -30,14 +31,20 @@
|
||||||
|
}
|
||||||
|
impl Handle {
|
||||||
|
fn is_file(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Channel { .. } | Self::Config { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
!self.is_file()
|
||||||
|
}
|
||||||
|
// TODO: capability rather than root
|
||||||
|
fn requires_root(&self) -> bool {
|
||||||
|
- matches!(self, Self::Access | Self::Channel { .. })
|
||||||
|
+ matches!(
|
||||||
|
+ self,
|
||||||
|
+ Self::Access | Self::Channel { .. } | Self::Config { .. }
|
||||||
|
+ )
|
||||||
|
}
|
||||||
|
fn is_scheme_root(&self) -> bool {
|
||||||
|
matches!(self, Self::SchemeRoot)
|
||||||
|
@@ -49,7 +56,8 @@
|
||||||
|
AwaitingResponseRead(VecDeque<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
-const DEVICE_CONTENTS: &[&str] = &["channel"];
|
||||||
|
+const DEVICE_CONTENTS: &[&str] = &["channel", "config"];
|
||||||
|
+const STANDARD_CONFIG_SPACE_SIZE: usize = 256;
|
||||||
|
|
||||||
|
impl PciScheme {
|
||||||
|
pub fn access(&mut self) -> usize {
|
||||||
|
@@ -133,6 +141,7 @@
|
||||||
|
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||||
|
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||||
|
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||||
|
+ Handle::Config { .. } => (STANDARD_CONFIG_SPACE_SIZE, MODE_CHR | 0o600),
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
stat.st_size = len as u64;
|
||||||
|
@@ -143,7 +152,7 @@
|
||||||
|
&mut self,
|
||||||
|
id: usize,
|
||||||
|
buf: &mut [u8],
|
||||||
|
- _offset: u64,
|
||||||
|
+ offset: u64,
|
||||||
|
_fcntl_flags: u32,
|
||||||
|
_ctx: &CallerCtx,
|
||||||
|
) -> Result<usize> {
|
||||||
|
@@ -160,6 +169,7 @@
|
||||||
|
addr: _,
|
||||||
|
ref mut st,
|
||||||
|
} => Self::read_channel(st, buf),
|
||||||
|
+ Handle::Config { addr } => Self::read_config(&self.pcie, addr, buf, offset),
|
||||||
|
Handle::SchemeRoot => Err(Error::new(EBADF)),
|
||||||
|
_ => Err(Error::new(EBADF)),
|
||||||
|
}
|
||||||
|
@@ -193,7 +203,9 @@
|
||||||
|
return Ok(buf);
|
||||||
|
}
|
||||||
|
Handle::Device => DEVICE_CONTENTS,
|
||||||
|
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||||
|
+ Handle::Access | Handle::Channel { .. } | Handle::Config { .. } => {
|
||||||
|
+ return Err(Error::new(ENOTDIR));
|
||||||
|
+ }
|
||||||
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -212,7 +224,7 @@
|
||||||
|
&mut self,
|
||||||
|
id: usize,
|
||||||
|
buf: &[u8],
|
||||||
|
- _offset: u64,
|
||||||
|
+ offset: u64,
|
||||||
|
_fcntl_flags: u32,
|
||||||
|
_ctx: &CallerCtx,
|
||||||
|
) -> Result<usize> {
|
||||||
|
@@ -226,6 +238,7 @@
|
||||||
|
Handle::Channel { addr, ref mut st } => {
|
||||||
|
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||||
|
}
|
||||||
|
+ Handle::Config { addr } => Self::write_config(&self.pcie, addr, buf, offset),
|
||||||
|
|
||||||
|
_ => Err(Error::new(EBADF)),
|
||||||
|
}
|
||||||
|
@@ -356,11 +369,73 @@
|
||||||
|
st: ChannelState::AwaitingData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ "config" => Handle::Config { addr },
|
||||||
|
_ => return Err(Error::new(ENOENT)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
+ fn read_config(pcie: &Pcie, addr: PciAddress, buf: &mut [u8], offset: u64) -> Result<usize> {
|
||||||
|
+ let start = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
|
||||||
|
+ let end = start.saturating_add(buf.len()).min(STANDARD_CONFIG_SPACE_SIZE);
|
||||||
|
+ if start >= end {
|
||||||
|
+ return Ok(0);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let aligned_start = start & !0x3;
|
||||||
|
+ let aligned_end = (end + 3) & !0x3;
|
||||||
|
+
|
||||||
|
+ let mut bytes_read = 0;
|
||||||
|
+ for dword_offset in (aligned_start..aligned_end).step_by(4) {
|
||||||
|
+ let bytes = unsafe { pcie.read(addr, dword_offset as u16) }.to_le_bytes();
|
||||||
|
+
|
||||||
|
+ let chunk_start = start.max(dword_offset);
|
||||||
|
+ let chunk_end = end.min(dword_offset + bytes.len());
|
||||||
|
+ let src_start = chunk_start - dword_offset;
|
||||||
|
+ let dst_start = chunk_start - start;
|
||||||
|
+ let len = chunk_end - chunk_start;
|
||||||
|
+
|
||||||
|
+ buf[dst_start..dst_start + len].copy_from_slice(&bytes[src_start..src_start + len]);
|
||||||
|
+ bytes_read += len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ Ok(bytes_read)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ fn write_config(pcie: &Pcie, addr: PciAddress, buf: &[u8], offset: u64) -> Result<usize> {
|
||||||
|
+ let start = usize::try_from(offset).map_err(|_| Error::new(EINVAL))?;
|
||||||
|
+ let end = start.saturating_add(buf.len()).min(STANDARD_CONFIG_SPACE_SIZE);
|
||||||
|
+ if start >= end {
|
||||||
|
+ return Ok(0);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ let aligned_start = start & !0x3;
|
||||||
|
+ let aligned_end = (end + 3) & !0x3;
|
||||||
|
+
|
||||||
|
+ let mut bytes_written = 0;
|
||||||
|
+ for dword_offset in (aligned_start..aligned_end).step_by(4) {
|
||||||
|
+ let chunk_start = start.max(dword_offset);
|
||||||
|
+ let chunk_end = end.min(dword_offset + 4);
|
||||||
|
+ let dst_start = chunk_start - dword_offset;
|
||||||
|
+ let src_start = chunk_start - start;
|
||||||
|
+ let len = chunk_end - chunk_start;
|
||||||
|
+
|
||||||
|
+ let mut bytes = if dst_start == 0 && len == 4 {
|
||||||
|
+ [0; 4]
|
||||||
|
+ } else {
|
||||||
|
+ unsafe { pcie.read(addr, dword_offset as u16) }.to_le_bytes()
|
||||||
|
+ };
|
||||||
|
+ bytes[dst_start..dst_start + len].copy_from_slice(&buf[src_start..src_start + len]);
|
||||||
|
+
|
||||||
|
+ unsafe {
|
||||||
|
+ pcie.write(addr, dword_offset as u16, u32::from_le_bytes(bytes));
|
||||||
|
+ }
|
||||||
|
+ bytes_written += len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ Ok(bytes_written)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
fn read_channel(state: &mut ChannelState, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
match *state {
|
||||||
|
ChannelState::AwaitingResponseRead(ref mut queue) => {
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,126 @@
|
|||||||
|
diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h
|
||||||
|
--- a/src/corelib/thread/qfutex_p.h
|
||||||
|
+++ b/src/corelib/thread/qfutex_p.h
|
||||||
|
@@ -41,6 +41,9 @@
|
||||||
|
#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
|
||||||
|
// use Linux mutexes everywhere except for LSB builds
|
||||||
|
# include "qfutex_linux_p.h"
|
||||||
|
+#elif defined(Q_OS_REDOX)
|
||||||
|
+// Redox kernel implements the same futex syscall as Linux (SYS_FUTEX = 202)
|
||||||
|
+# include "qfutex_redox_p.h"
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
# include "qfutex_win_p.h"
|
||||||
|
#else
|
||||||
|
diff --git a/src/corelib/thread/qfutex_redox_p.h b/src/corelib/thread/qfutex_redox_p.h
|
||||||
|
new file mode 100644
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/corelib/thread/qfutex_redox_p.h
|
||||||
|
@@ -0,0 +1,97 @@
|
||||||
|
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||||
|
+
|
||||||
|
+#ifndef QFUTEX_REDOX_P_H
|
||||||
|
+#define QFUTEX_REDOX_P_H
|
||||||
|
+
|
||||||
|
+#include <qdeadlinetimer.h>
|
||||||
|
+#include <qtsan_impl.h>
|
||||||
|
+
|
||||||
|
+#include <errno.h>
|
||||||
|
+#include <limits.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+
|
||||||
|
+#define QT_ALWAYS_USE_FUTEX
|
||||||
|
+
|
||||||
|
+#ifndef __NR_futex
|
||||||
|
+# define __NR_futex 202
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+QT_BEGIN_NAMESPACE
|
||||||
|
+
|
||||||
|
+namespace QtRedoxFutex {
|
||||||
|
+
|
||||||
|
+constexpr inline bool futexAvailable() { return true; }
|
||||||
|
+
|
||||||
|
+constexpr int RedoxFutexWait = 0;
|
||||||
|
+constexpr int RedoxFutexWake = 1;
|
||||||
|
+
|
||||||
|
+inline long redox_syscall6(long nr, long a1, long a2, long a3, long a4, long a5, long a6) noexcept
|
||||||
|
+{
|
||||||
|
+ register long r10 __asm__("r10") = a4;
|
||||||
|
+ register long r8 __asm__("r8") = a5;
|
||||||
|
+ register long r9 __asm__("r9") = a6;
|
||||||
|
+ long ret;
|
||||||
|
+ __asm__ __volatile__(
|
||||||
|
+ "syscall"
|
||||||
|
+ : "=a"(ret)
|
||||||
|
+ : "a"(nr), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
|
||||||
|
+ : "rcx", "r11", "memory"
|
||||||
|
+ );
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+inline long _q_futex(int *addr, int op, int val, quintptr val2 = 0,
|
||||||
|
+ int *addr2 = nullptr, int val3 = 0) noexcept
|
||||||
|
+{
|
||||||
|
+ QtTsan::futexRelease(addr, addr2);
|
||||||
|
+ long result = redox_syscall6(__NR_futex, (long)addr, (long)op, (long)val, (long)val2, (long)addr2, (long)val3);
|
||||||
|
+ QtTsan::futexAcquire(addr, addr2);
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+template <typename T> int *addr(T *ptr)
|
||||||
|
+{
|
||||||
|
+ int *int_addr = reinterpret_cast<int *>(ptr);
|
||||||
|
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||||
|
+ if (sizeof(T) > sizeof(int))
|
||||||
|
+ int_addr++;
|
||||||
|
+#endif
|
||||||
|
+ return int_addr;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+template <typename Atomic>
|
||||||
|
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
|
||||||
|
+{
|
||||||
|
+ _q_futex(addr(&futex), RedoxFutexWait, qintptr(expectedValue));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+template <typename Atomic>
|
||||||
|
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
|
||||||
|
+{
|
||||||
|
+ auto timeout = deadline.deadline<std::chrono::steady_clock>().time_since_epoch();
|
||||||
|
+ struct timespec ts;
|
||||||
|
+ ts.tv_sec = timeout.count() < 0 ? 0 : static_cast<long>(std::chrono::duration_cast<std::chrono::seconds>(timeout).count());
|
||||||
|
+ auto ns_remainder = timeout - std::chrono::seconds(ts.tv_sec);
|
||||||
|
+ ts.tv_nsec = static_cast<long>(std::chrono::duration_cast<std::chrono::nanoseconds>(ns_remainder).count());
|
||||||
|
+ long r = _q_futex(addr(&futex), RedoxFutexWait, qintptr(expectedValue), quintptr(&ts),
|
||||||
|
+ nullptr, 0);
|
||||||
|
+ return r == 0 || errno != ETIMEDOUT;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
|
||||||
|
+{
|
||||||
|
+ _q_futex(addr(&futex), RedoxFutexWake, 1);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
|
||||||
|
+{
|
||||||
|
+ _q_futex(addr(&futex), RedoxFutexWake, INT_MAX);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+} // namespace QtRedoxFutex
|
||||||
|
+
|
||||||
|
+namespace QtFutex = QtRedoxFutex;
|
||||||
|
+
|
||||||
|
+QT_END_NAMESPACE
|
||||||
|
+
|
||||||
|
+#endif // QFUTEX_REDOX_P_H
|
||||||
|
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
|
||||||
|
--- a/src/corelib/thread/qmutex.h
|
||||||
|
+++ b/src/corelib/thread/qmutex.h
|
||||||
|
@@ -26,6 +26,6 @@
|
||||||
|
protected:
|
||||||
|
static constexpr bool FutexAlwaysAvailable =
|
||||||
|
-#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
|
||||||
|
+#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(Q_OS_REDOX) // these platforms use futex
|
||||||
|
true
|
||||||
|
#else
|
||||||
|
false
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
|
||||||
|
index 4a01b0d9..00271fa9 100644
|
||||||
|
--- a/src/ld_so/linker.rs
|
||||||
|
+++ b/src/ld_so/linker.rs
|
||||||
|
@@ -928,6 +928,7 @@ impl Linker {
|
||||||
|
search_paths.extend(ld_path.split(PATH_SEP));
|
||||||
|
}
|
||||||
|
search_paths.push("/lib");
|
||||||
|
+ search_paths.push("/usr/lib");
|
||||||
|
for part in search_paths.iter() {
|
||||||
|
full_path = format!("{}/{}", part, name);
|
||||||
|
if debug {
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs
|
||||||
|
--- a/src/header/stdlib/mod.rs
|
||||||
|
+++ b/src/header/stdlib/mod.rs
|
||||||
|
@@ -31,8 +31,8 @@ use crate::{
|
||||||
|
platform::{
|
||||||
|
self, Pal, Sys,
|
||||||
|
types::{
|
||||||
|
- c_char, c_double, c_float, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong,
|
||||||
|
- c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t,
|
||||||
|
+ c_char, c_double, c_float, c_int, c_long, c_longdouble, c_longlong, c_uint, c_ulong,
|
||||||
|
+ c_ulonglong, c_ushort, c_void, size_t, ssize_t, uintptr_t, wchar_t,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
raw_cell::RawCell,
|
||||||
|
@@ -1506,7 +1506,19 @@ pub unsafe extern "C" fn strtol(s: *const c_char, endptr: *mut *mut c_char, base
|
||||||
|
strto_impl!(c_long, true, c_long::MAX, c_long::MIN, s, endptr, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
-// TODO: strtold(), when long double is available
|
||||||
|
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtod.html>.
|
||||||
|
+///
|
||||||
|
+/// Note: delegates to strtod and converts via relibc_dtold because c_longdouble
|
||||||
|
+/// is u128 (soft float) on this target. Precision is limited to double.
|
||||||
|
+#[unsafe(no_mangle)]
|
||||||
|
+pub unsafe extern "C" fn strtold(s: *const c_char, endptr: *mut *mut c_char) -> c_longdouble {
|
||||||
|
+ let mut result: c_longdouble = 0;
|
||||||
|
+ unsafe {
|
||||||
|
+ let d = strtod(s, endptr);
|
||||||
|
+ crate::header::stdio::printf::relibc_dtold(d, &mut result);
|
||||||
|
+ }
|
||||||
|
+ result
|
||||||
|
+}
|
||||||
|
|
||||||
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtol.html>.
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
diff --git a/src/header/sys_ioctl/redox/mod.rs b/src/header/sys_ioctl/redox/mod.rs
|
||||||
|
index 8b3cf8f..c3e925e 100644
|
||||||
|
--- a/src/header/sys_ioctl/redox/mod.rs
|
||||||
|
+++ b/src/header/sys_ioctl/redox/mod.rs
|
||||||
|
@@ -4,7 +4,7 @@ use syscall;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{Errno, Result, ResultExt},
|
||||||
|
- header::{errno::EINVAL, fcntl, termios},
|
||||||
|
+ header::{errno::{EINVAL, ENOTTY}, fcntl, termios},
|
||||||
|
platform::{
|
||||||
|
Pal, Sys,
|
||||||
|
types::{c_int, c_ulong, c_ulonglong, c_void, pid_t},
|
||||||
|
@@ -166,25 +166,9 @@ unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Result<c
|
||||||
|
todo_skip!(0, "ioctl SIOCATMARK");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
- // See https://docs.kernel.org/userspace-api/ioctl/ioctl-decoding.html for details
|
||||||
|
- let dir = (request >> 30) & 0b11;
|
||||||
|
- let size = ((request >> 16) & 0x3FFF) as usize;
|
||||||
|
- let name = (((request >> 8) & 0xFF) as u8) as char;
|
||||||
|
- let func = (request & 0xFF) as u8;
|
||||||
|
- match name {
|
||||||
|
- 'd' => {
|
||||||
|
- let buf = match dir {
|
||||||
|
- 0b10 => IoctlBuffer::Read(out, size),
|
||||||
|
- 0b01 => IoctlBuffer::Write(out, size),
|
||||||
|
- 0b11 => IoctlBuffer::ReadWrite(out, size),
|
||||||
|
- _ => IoctlBuffer::None,
|
||||||
|
- };
|
||||||
|
- return unsafe { drm::ioctl(fd, func, buf) };
|
||||||
|
- }
|
||||||
|
- _ => {
|
||||||
|
- return Err(Errno(EINVAL));
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
+ // libdrm handles DRM ioctls via redox_drm_simple_ioctl() with
|
||||||
|
+ // its own scheme:drm wire format; relibc must not intercept.
|
||||||
|
+ return Err(Errno(ENOTTY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(0)
|
||||||
@@ -1,7 +1,17 @@
|
|||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use crate::{DriverError, Result};
|
use crate::{DriverError, Result};
|
||||||
|
|
||||||
|
/// Method for accessing PCI configuration space.
|
||||||
|
enum ConfigAccess {
|
||||||
|
/// Access via /scheme/pci/.../config file (normal path)
|
||||||
|
SchemeFile(std::fs::File),
|
||||||
|
/// Access via x86 I/O ports 0xCF8/0xCFC (fallback when scheme:pci is stale)
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
IoPorts,
|
||||||
|
}
|
||||||
|
|
||||||
pub const PCI_VENDOR_ID_AMD: u16 = 0x1002;
|
pub const PCI_VENDOR_ID_AMD: u16 = 0x1002;
|
||||||
pub const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
|
pub const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
|
||||||
pub const PCI_VENDOR_ID_NVIDIA: u16 = 0x10DE;
|
pub const PCI_VENDOR_ID_NVIDIA: u16 = 0x10DE;
|
||||||
@@ -11,6 +21,10 @@ pub const PCI_CLASS_DISPLAY_VGA: u8 = 0x00;
|
|||||||
pub const PCI_CLASS_DISPLAY_3D: u8 = 0x02;
|
pub const PCI_CLASS_DISPLAY_3D: u8 = 0x02;
|
||||||
|
|
||||||
pub const PCI_HEADER_TYPE_NORMAL: u8 = 0x00;
|
pub const PCI_HEADER_TYPE_NORMAL: u8 = 0x00;
|
||||||
|
const PCI_CONFIG_ADDRESS_PORT: u16 = 0xCF8;
|
||||||
|
const PCI_CONFIG_DATA_PORT: u16 = 0xCFC;
|
||||||
|
const PCI_CONFIG_SPACE_SIZE: usize = 256;
|
||||||
|
static PCI_IOPL_INIT: OnceLock<std::result::Result<(), String>> = OnceLock::new();
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub struct PciLocation {
|
pub struct PciLocation {
|
||||||
@@ -241,7 +255,7 @@ impl PciDeviceInfo {
|
|||||||
|
|
||||||
pub struct PciDevice {
|
pub struct PciDevice {
|
||||||
location: PciLocation,
|
location: PciLocation,
|
||||||
config_fd: std::fs::File,
|
access: ConfigAccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PciDevice {
|
impl PciDevice {
|
||||||
@@ -257,16 +271,31 @@ impl PciDevice {
|
|||||||
|
|
||||||
pub fn open_location(loc: &PciLocation) -> Result<Self> {
|
pub fn open_location(loc: &PciLocation) -> Result<Self> {
|
||||||
let config_path = format!("{}/config", loc.scheme_path());
|
let config_path = format!("{}/config", loc.scheme_path());
|
||||||
let fd = std::fs::OpenOptions::new()
|
let access = match std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(&config_path)
|
.open(&config_path)
|
||||||
.map_err(|e| {
|
{
|
||||||
DriverError::Pci(format!("cannot open PCI config at {}: {}", config_path, e))
|
Ok(fd) => ConfigAccess::SchemeFile(fd),
|
||||||
})?;
|
Err(err) => {
|
||||||
|
ensure_io_port_access().map_err(|fallback_err| {
|
||||||
|
DriverError::Pci(format!(
|
||||||
|
"cannot open PCI config at {}: {}; I/O port fallback unavailable: {}",
|
||||||
|
config_path, err, fallback_err
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
log::debug!(
|
||||||
|
"PCI: scheme path {} unavailable, using I/O port fallback for {}",
|
||||||
|
config_path,
|
||||||
|
loc
|
||||||
|
);
|
||||||
|
ConfigAccess::IoPorts
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(PciDevice {
|
Ok(PciDevice {
|
||||||
location: *loc,
|
location: *loc,
|
||||||
config_fd: fd,
|
access,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,42 +308,57 @@ impl PciDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_config_dword(&mut self, offset: u64) -> Result<u32> {
|
pub fn read_config_dword(&mut self, offset: u64) -> Result<u32> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
let mut buf = [0u8; 4];
|
ConfigAccess::SchemeFile(fd) => {
|
||||||
self.config_fd.read_exact(&mut buf)?;
|
let mut buf = [0u8; 4];
|
||||||
Ok(u32::from_le_bytes(buf))
|
read_scheme_config(fd, offset, &mut buf)?;
|
||||||
|
Ok(u32::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
ConfigAccess::IoPorts => io_read_config_dword(&self.location, offset),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_config_word(&mut self, offset: u64) -> Result<u16> {
|
pub fn read_config_word(&mut self, offset: u64) -> Result<u16> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
let mut buf = [0u8; 2];
|
ConfigAccess::SchemeFile(fd) => {
|
||||||
self.config_fd.read_exact(&mut buf)?;
|
let mut buf = [0u8; 2];
|
||||||
Ok(u16::from_le_bytes(buf))
|
read_scheme_config(fd, offset, &mut buf)?;
|
||||||
|
Ok(u16::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
ConfigAccess::IoPorts => io_read_config_word(&self.location, offset),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_config_byte(&mut self, offset: u64) -> Result<u8> {
|
pub fn read_config_byte(&mut self, offset: u64) -> Result<u8> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
let mut buf = [0u8; 1];
|
ConfigAccess::SchemeFile(fd) => {
|
||||||
self.config_fd.read_exact(&mut buf)?;
|
let mut buf = [0u8; 1];
|
||||||
Ok(buf[0])
|
read_scheme_config(fd, offset, &mut buf)?;
|
||||||
|
Ok(buf[0])
|
||||||
|
}
|
||||||
|
ConfigAccess::IoPorts => io_read_config_byte(&self.location, offset),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_config_dword(&mut self, offset: u64, val: u32) -> Result<()> {
|
pub fn write_config_dword(&mut self, offset: u64, val: u32) -> Result<()> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
self.config_fd.write_all(&val.to_le_bytes())?;
|
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &val.to_le_bytes()),
|
||||||
Ok(())
|
ConfigAccess::IoPorts => io_write_config_dword(&self.location, offset, val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_config_word(&mut self, offset: u64, val: u16) -> Result<()> {
|
pub fn write_config_word(&mut self, offset: u64, val: u16) -> Result<()> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
self.config_fd.write_all(&val.to_le_bytes())?;
|
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &val.to_le_bytes()),
|
||||||
Ok(())
|
ConfigAccess::IoPorts => io_write_config_word(&self.location, offset, val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_config_byte(&mut self, offset: u64, val: u8) -> Result<()> {
|
pub fn write_config_byte(&mut self, offset: u64, val: u8) -> Result<()> {
|
||||||
self.config_fd.seek(SeekFrom::Start(offset))?;
|
match &mut self.access {
|
||||||
self.config_fd.write_all(&[val])?;
|
ConfigAccess::SchemeFile(fd) => write_scheme_config(fd, offset, &[val]),
|
||||||
Ok(())
|
ConfigAccess::IoPorts => io_write_config_byte(&self.location, offset, val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vendor_id(&mut self) -> Result<u16> {
|
pub fn vendor_id(&mut self) -> Result<u16> {
|
||||||
@@ -657,34 +701,343 @@ impl PciDevice {
|
|||||||
|
|
||||||
impl std::io::Write for PciDevice {
|
impl std::io::Write for PciDevice {
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
self.config_fd.write(buf)
|
match &mut self.access {
|
||||||
|
ConfigAccess::SchemeFile(fd) => fd.write(buf),
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
ConfigAccess::IoPorts => Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Unsupported,
|
||||||
|
"raw write not supported for I/O port PCI config access",
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> {
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
self.config_fd.flush()
|
match &mut self.access {
|
||||||
|
ConfigAccess::SchemeFile(fd) => fd.flush(),
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
ConfigAccess::IoPorts => Ok(()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_scheme_config(fd: &mut std::fs::File, offset: u64, buf: &mut [u8]) -> Result<()> {
|
||||||
|
fd.seek(SeekFrom::Start(offset))?;
|
||||||
|
fd.read_exact(buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_scheme_config(fd: &mut std::fs::File, offset: u64, buf: &[u8]) -> Result<()> {
|
||||||
|
fd.seek(SeekFrom::Start(offset))?;
|
||||||
|
fd.write_all(buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_io_port_access() -> Result<()> {
|
||||||
|
match PCI_IOPL_INIT.get_or_init(init_io_port_access) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(err) => Err(DriverError::Pci(err.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_io_port_access() -> std::result::Result<(), String> {
|
||||||
|
#[cfg(all(target_arch = "x86_64", target_os = "redox"))]
|
||||||
|
{
|
||||||
|
crate::io::acquire_iopl().map_err(|err| {
|
||||||
|
format!(
|
||||||
|
"cannot acquire I/O privilege level for PCI config fallback: {}",
|
||||||
|
err
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", not(target_os = "redox")))]
|
||||||
|
{
|
||||||
|
Err(String::from(
|
||||||
|
"PCI I/O port fallback requires Redox I/O privilege and is unavailable on non-Redox targets",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
{
|
||||||
|
Err(String::from(
|
||||||
|
"PCI I/O port fallback is only supported on x86_64",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_read_config_dword(location: &PciLocation, offset: u64) -> Result<u32> {
|
||||||
|
if offset & 0x3 == 0 {
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
Ok(inl(PCI_CONFIG_DATA_PORT))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
for (index, byte) in buf.iter_mut().enumerate() {
|
||||||
|
*byte = io_read_config_byte(location, offset + index as u64)?;
|
||||||
|
}
|
||||||
|
Ok(u32::from_le_bytes(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_read_config_word(location: &PciLocation, offset: u64) -> Result<u16> {
|
||||||
|
let low_bits = (offset & 0x3) as u16;
|
||||||
|
if low_bits <= 2 {
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
Ok(inw(PCI_CONFIG_DATA_PORT + low_bits))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let lo = io_read_config_byte(location, offset)?;
|
||||||
|
let hi = io_read_config_byte(location, offset + 1)?;
|
||||||
|
Ok(u16::from_le_bytes([lo, hi]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_read_config_byte(location: &PciLocation, offset: u64) -> Result<u8> {
|
||||||
|
let low_bits = (offset & 0x3) as u16;
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
Ok(inb(PCI_CONFIG_DATA_PORT + low_bits))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_write_config_dword(location: &PciLocation, offset: u64, val: u32) -> Result<()> {
|
||||||
|
if offset & 0x3 == 0 {
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
outl(PCI_CONFIG_DATA_PORT, val);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
for (index, byte) in val.to_le_bytes().into_iter().enumerate() {
|
||||||
|
io_write_config_byte(location, offset + index as u64, byte)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_write_config_word(location: &PciLocation, offset: u64, val: u16) -> Result<()> {
|
||||||
|
let low_bits = (offset & 0x3) as u16;
|
||||||
|
if low_bits <= 2 {
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
outw(PCI_CONFIG_DATA_PORT + low_bits, val);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
let [lo, hi] = val.to_le_bytes();
|
||||||
|
io_write_config_byte(location, offset, lo)?;
|
||||||
|
io_write_config_byte(location, offset + 1, hi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn io_write_config_byte(location: &PciLocation, offset: u64, val: u8) -> Result<()> {
|
||||||
|
let low_bits = (offset & 0x3) as u16;
|
||||||
|
let address = pci_config_address(location, offset)?;
|
||||||
|
unsafe {
|
||||||
|
outl(PCI_CONFIG_ADDRESS_PORT, address);
|
||||||
|
outb(PCI_CONFIG_DATA_PORT + low_bits, val);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pci_config_address(location: &PciLocation, offset: u64) -> Result<u32> {
|
||||||
|
let register = u32::try_from(offset)
|
||||||
|
.map_err(|_| DriverError::InvalidParam("PCI config offset exceeds u32"))?;
|
||||||
|
Ok(
|
||||||
|
0x8000_0000
|
||||||
|
| ((location.bus as u32) << 16)
|
||||||
|
| ((location.device as u32) << 11)
|
||||||
|
| ((location.function as u32) << 8)
|
||||||
|
| (register & 0xFC),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inb(port: u16) -> u8 {
|
||||||
|
let val: u8;
|
||||||
|
core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack));
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outb(port: u16, val: u8) {
|
||||||
|
core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inw(port: u16) -> u16 {
|
||||||
|
let val: u16;
|
||||||
|
core::arch::asm!("in ax, dx", in("dx") port, out("ax") val, options(nomem, nostack));
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outw(port: u16, val: u16) {
|
||||||
|
core::arch::asm!("out dx, ax", in("dx") port, in("ax") val, options(nomem, nostack));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inl(port: u16) -> u32 {
|
||||||
|
let val: u32;
|
||||||
|
core::arch::asm!("in eax, dx", in("dx") port, out("eax") val, options(nomem, nostack));
|
||||||
|
val
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outl(port: u16, val: u32) {
|
||||||
|
core::arch::asm!("out dx, eax", in("dx") port, in("eax") val, options(nomem, nostack));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inb(_port: u16) -> u8 {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outb(_port: u16, _val: u8) {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inw(_port: u16) -> u16 {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outw(_port: u16, _val: u16) {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn inl(_port: u16) -> u32 {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn outl(_port: u16, _val: u32) {
|
||||||
|
unreachable!("PCI I/O port access is only built for x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||||
let entries = std::fs::read_dir("/scheme/pci")?;
|
let class_desc = class
|
||||||
|
.map(|class| format!(" for class {class:#04x}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let devices = match std::fs::read_dir("/scheme/pci") {
|
||||||
|
Ok(entries) => {
|
||||||
|
let mut devices = Vec::new();
|
||||||
|
let mut saw_entry = false;
|
||||||
|
|
||||||
|
for entry in entries {
|
||||||
|
let entry = entry?;
|
||||||
|
saw_entry = true;
|
||||||
|
|
||||||
|
let name = entry.file_name();
|
||||||
|
let Some(name_str) = name.to_str() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// pcid scheme entries use format: segment--bus--device.function
|
||||||
|
let Some(location) = parse_scheme_entry(name_str) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(data) = read_pci_config_space(location)? {
|
||||||
|
if let Some(info) = parse_device_info_from_config_space(location, &data)
|
||||||
|
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||||
|
{
|
||||||
|
devices.push(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if saw_entry {
|
||||||
|
devices
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"PCI enumeration{}: /scheme/pci is empty, falling back to direct probing",
|
||||||
|
class_desc
|
||||||
|
);
|
||||||
|
direct_probe_pci_bus(class)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!(
|
||||||
|
"PCI enumeration{}: cannot read /scheme/pci ({}), falling back to direct probing",
|
||||||
|
class_desc,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
direct_probe_pci_bus(class)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"PCI enumeration{}: found {} devices",
|
||||||
|
class_desc,
|
||||||
|
devices.len()
|
||||||
|
);
|
||||||
|
Ok(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn direct_probe_pci_bus(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
||||||
let mut devices = Vec::new();
|
let mut devices = Vec::new();
|
||||||
|
|
||||||
for entry in entries {
|
for device in 0..=31 {
|
||||||
let entry = entry?;
|
let function_zero = PciLocation {
|
||||||
let name = entry.file_name();
|
segment: 0,
|
||||||
let name_str = match name.to_str() {
|
bus: 0,
|
||||||
Some(s) => s,
|
device,
|
||||||
None => continue,
|
function: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// pcid scheme entries use format: segment--bus--device.function
|
let Some(data) = read_pci_config_space(function_zero)? else {
|
||||||
let location = match parse_scheme_entry(name_str) {
|
continue;
|
||||||
Some(loc) => loc,
|
|
||||||
None => continue,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let config_path = format!("{}/config", location.scheme_path());
|
let is_multifunction = data.get(0x0e).is_some_and(|header_type| header_type & 0x80 != 0);
|
||||||
if let Ok(data) = std::fs::read(&config_path) {
|
|
||||||
|
if let Some(info) = parse_device_info_from_config_space(function_zero, &data)
|
||||||
|
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||||
|
{
|
||||||
|
devices.push(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_multifunction {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for function in 1..=7 {
|
||||||
|
let location = PciLocation {
|
||||||
|
segment: 0,
|
||||||
|
bus: 0,
|
||||||
|
device,
|
||||||
|
function,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(data) = read_pci_config_space(location)? else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(info) = parse_device_info_from_config_space(location, &data)
|
if let Some(info) = parse_device_info_from_config_space(location, &data)
|
||||||
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
.filter(|info| class.is_none_or(|class| info.class_code == class))
|
||||||
{
|
{
|
||||||
@@ -693,17 +1046,32 @@ fn enumerate_pci_filtered(class: Option<u8>) -> Result<Vec<PciDeviceInfo>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!(
|
|
||||||
"PCI enumeration{}: found {} devices",
|
|
||||||
class
|
|
||||||
.map(|class| format!(" for class {class:#04x}"))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
devices.len()
|
|
||||||
);
|
|
||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_device_info_from_config_space(location: PciLocation, data: &[u8]) -> Option<PciDeviceInfo> {
|
fn read_pci_config_space(location: PciLocation) -> Result<Option<Vec<u8>>> {
|
||||||
|
// Try scheme:pci file first, then fall back to I/O ports via PciDevice.
|
||||||
|
let mut device = match PciDevice::open_location(&location) {
|
||||||
|
Ok(dev) => dev,
|
||||||
|
Err(_) => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
if device.vendor_id()? == 0xFFFF {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = vec![0u8; PCI_CONFIG_SPACE_SIZE];
|
||||||
|
for offset in (0..PCI_CONFIG_SPACE_SIZE).step_by(4) {
|
||||||
|
let dword = device.read_config_dword(offset as u64)?;
|
||||||
|
data[offset..offset + 4].copy_from_slice(&dword.to_le_bytes());
|
||||||
|
}
|
||||||
|
Ok(Some(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_device_info_from_config_space(
|
||||||
|
location: PciLocation,
|
||||||
|
data: &[u8],
|
||||||
|
) -> Option<PciDeviceInfo> {
|
||||||
if data.len() < 64 {
|
if data.len() < 64 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -1055,4 +1423,17 @@ mod tests {
|
|||||||
assert_eq!(info.interrupt_support(), InterruptSupport::LegacyOnly);
|
assert_eq!(info.interrupt_support(), InterruptSupport::LegacyOnly);
|
||||||
assert_eq!(info.interrupt_support().as_str(), "legacy");
|
assert_eq!(info.interrupt_support().as_str(), "legacy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pci_config_address_encodes_bus_device_function_and_register() {
|
||||||
|
let location = PciLocation {
|
||||||
|
segment: 0,
|
||||||
|
bus: 0x80,
|
||||||
|
device: 0x1f,
|
||||||
|
function: 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
let address = pci_config_address(&location, 0x15).expect("PCI address should encode");
|
||||||
|
assert_eq!(address, 0x8080_fb14);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,23 @@ pub struct RedoxPrivateCsWaitResult {
|
|||||||
pub completed_seqno: u64,
|
pub completed_seqno: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minimal virgl response types for the GpuDriver trait.
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
pub struct VirglCapsetInfo {
|
||||||
|
pub capset_id: u32,
|
||||||
|
pub capset_max_version: u32,
|
||||||
|
pub capset_max_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VirglCapset {
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DriverError {
|
pub enum DriverError {
|
||||||
#[error("driver initialization failed: {0}")]
|
#[error("driver initialization failed: {0}")]
|
||||||
@@ -86,7 +103,7 @@ pub trait GpuDriver: Send + Sync {
|
|||||||
fn page_flip(&self, crtc_id: u32, fb_handle: u32, flags: u32) -> Result<u64>;
|
fn page_flip(&self, crtc_id: u32, fb_handle: u32, flags: u32) -> Result<u64>;
|
||||||
fn get_vblank(&self, crtc_id: u32) -> Result<u64>;
|
fn get_vblank(&self, crtc_id: u32) -> Result<u64>;
|
||||||
|
|
||||||
fn gem_create(&self, size: u64) -> Result<GemHandle>;
|
fn gem_create(&self, size: u64, width: u32, height: u32) -> Result<GemHandle>;
|
||||||
fn gem_close(&self, handle: GemHandle) -> Result<()>;
|
fn gem_close(&self, handle: GemHandle) -> Result<()>;
|
||||||
fn gem_mmap(&self, handle: GemHandle) -> Result<usize>;
|
fn gem_mmap(&self, handle: GemHandle) -> Result<usize>;
|
||||||
fn gem_size(&self, handle: GemHandle) -> Result<u64>;
|
fn gem_size(&self, handle: GemHandle) -> Result<u64>;
|
||||||
@@ -112,6 +129,93 @@ pub trait GpuDriver: Send + Sync {
|
|||||||
"private command completion waits are unavailable on this backend",
|
"private command completion waits are unavailable on this backend",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_virgl_3d(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_get_capset_info(&self, _capset_index: u32) -> Result<VirglCapsetInfo> {
|
||||||
|
Err(DriverError::Unsupported("virgl capset info"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_get_capset(&self, _capset_id: u32, _capset_version: u32) -> Result<VirglCapset> {
|
||||||
|
Err(DriverError::Unsupported("virgl capset"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_ctx_create(&self, _ctx_id: u32, _debug_name: &str, _context_init: u32) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl context creation"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_ctx_destroy(&self, _ctx_id: u32) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl context destruction"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_resource_create_3d(
|
||||||
|
&self,
|
||||||
|
_resource_id: u32,
|
||||||
|
_target: u32,
|
||||||
|
_format: u32,
|
||||||
|
_bind: u32,
|
||||||
|
_width: u32,
|
||||||
|
_height: u32,
|
||||||
|
_depth: u32,
|
||||||
|
_array_size: u32,
|
||||||
|
_last_level: u32,
|
||||||
|
_nr_samples: u32,
|
||||||
|
_flags: u32,
|
||||||
|
_ctx_id: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl 3D resource creation"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_submit_3d(&self, _ctx_id: u32, _command_data: &[u8]) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl submit 3D"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_transfer_to_host_3d(
|
||||||
|
&self,
|
||||||
|
_ctx_id: u32,
|
||||||
|
_resource_id: u32,
|
||||||
|
_x: u32,
|
||||||
|
_y: u32,
|
||||||
|
_z: u32,
|
||||||
|
_w: u32,
|
||||||
|
_h: u32,
|
||||||
|
_d: u32,
|
||||||
|
_offset: u64,
|
||||||
|
_level: u32,
|
||||||
|
_stride: u32,
|
||||||
|
_layer_stride: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl transfer to host"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_transfer_from_host_3d(
|
||||||
|
&self,
|
||||||
|
_ctx_id: u32,
|
||||||
|
_resource_id: u32,
|
||||||
|
_x: u32,
|
||||||
|
_y: u32,
|
||||||
|
_z: u32,
|
||||||
|
_w: u32,
|
||||||
|
_h: u32,
|
||||||
|
_d: u32,
|
||||||
|
_offset: u64,
|
||||||
|
_level: u32,
|
||||||
|
_stride: u32,
|
||||||
|
_layer_stride: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl transfer from host"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virgl_resource_attach_backing(
|
||||||
|
&self,
|
||||||
|
_resource_id: u32,
|
||||||
|
_phys_addr: u64,
|
||||||
|
_length: u32,
|
||||||
|
) -> Result<()> {
|
||||||
|
Err(DriverError::Unsupported("virgl resource attach backing"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -473,7 +473,7 @@ impl GpuDriver for AmdDriver {
|
|||||||
Ok(self.vblank_count.load(Ordering::SeqCst))
|
Ok(self.vblank_count.load(Ordering::SeqCst))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gem_create(&self, size: u64) -> Result<GemHandle> {
|
fn gem_create(&self, size: u64, _width: u32, _height: u32) -> Result<GemHandle> {
|
||||||
let mut gem = self
|
let mut gem = self
|
||||||
.gem
|
.gem
|
||||||
.lock()
|
.lock()
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ impl GpuDriver for IntelDriver {
|
|||||||
Ok(self.vblank_count.load(Ordering::SeqCst))
|
Ok(self.vblank_count.load(Ordering::SeqCst))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gem_create(&self, size: u64) -> Result<GemHandle> {
|
fn gem_create(&self, size: u64, _width: u32, _height: u32) -> Result<GemHandle> {
|
||||||
let handle = {
|
let handle = {
|
||||||
let mut gem = self
|
let mut gem = self
|
||||||
.gem
|
.gem
|
||||||
|
|||||||
@@ -0,0 +1,404 @@
|
|||||||
|
use core::mem::size_of;
|
||||||
|
|
||||||
|
use crate::driver::{DriverError, Result};
|
||||||
|
|
||||||
|
pub const VIRTIO_GPU_F_EDID: u64 = 1 << 1;
|
||||||
|
pub const VIRTIO_GPU_F_VIRGL: u64 = 1 << 0;
|
||||||
|
pub const VIRTIO_GPU_F_RESOURCE_UUID: u64 = 1 << 2;
|
||||||
|
pub const VIRTIO_GPU_F_RESOURCE_BLOB: u64 = 1 << 3;
|
||||||
|
pub const VIRTIO_GPU_F_CONTEXT_INIT: u64 = 1 << 4;
|
||||||
|
pub const VIRTIO_F_VERSION_1: u64 = 1 << 32;
|
||||||
|
|
||||||
|
pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
|
||||||
|
pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0101;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0102;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0103;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
|
||||||
|
pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
|
||||||
|
pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x0107;
|
||||||
|
pub const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x0108;
|
||||||
|
pub const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x0109;
|
||||||
|
pub const VIRTIO_GPU_CMD_CTX_CREATE: u32 = 0x0200;
|
||||||
|
pub const VIRTIO_GPU_CMD_CTX_DESTROY: u32 = 0x0201;
|
||||||
|
pub const VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: u32 = 0x0202;
|
||||||
|
pub const VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: u32 = 0x0203;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: u32 = 0x0204;
|
||||||
|
pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: u32 = 0x0205;
|
||||||
|
pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x0206;
|
||||||
|
pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x0207;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB: u32 = 0x0208;
|
||||||
|
pub const VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB: u32 = 0x0209;
|
||||||
|
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_MAP_INFO: u32 = 0x1106;
|
||||||
|
pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1107;
|
||||||
|
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
|
||||||
|
pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
|
||||||
|
|
||||||
|
pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
|
||||||
|
pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
|
||||||
|
pub const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
|
||||||
|
pub const VIRTIO_GPU_CAPSET_VIRGL: u32 = 1;
|
||||||
|
pub const VIRTIO_GPU_CAPSET_VIRGL2: u32 = 2;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuCtrlHeader {
|
||||||
|
pub type_: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub fence_id: u64,
|
||||||
|
pub ctx_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtioGpuCtrlHeader {
|
||||||
|
pub fn command(type_: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
type_,
|
||||||
|
flags: 0,
|
||||||
|
fence_id: 0,
|
||||||
|
ctx_id: 0,
|
||||||
|
padding: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuRect {
|
||||||
|
pub x: u32,
|
||||||
|
pub y: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuBox {
|
||||||
|
pub x: u32,
|
||||||
|
pub y: u32,
|
||||||
|
pub z: u32,
|
||||||
|
pub w: u32,
|
||||||
|
pub h: u32,
|
||||||
|
pub d: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceUnref {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceCreate2d {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub format: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuSetScanout {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub rect: VirtioGpuRect,
|
||||||
|
pub scanout_id: u32,
|
||||||
|
pub resource_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceFlush {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub rect: VirtioGpuRect,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuTransferToHost2d {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub rect: VirtioGpuRect,
|
||||||
|
pub offset: u64,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuTransferHost3d {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub box_: VirtioGpuBox,
|
||||||
|
pub offset: u64,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub level: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub layer_stride: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuMemEntry {
|
||||||
|
pub addr: u64,
|
||||||
|
pub length: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceAttachBacking {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub nr_entries: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceCreate3d {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub target: u32,
|
||||||
|
pub format: u32,
|
||||||
|
pub bind: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub depth: u32,
|
||||||
|
pub array_size: u32,
|
||||||
|
pub last_level: u32,
|
||||||
|
pub nr_samples: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VirtioGpuCtxCreate {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub nlen: u32,
|
||||||
|
pub context_init: u32,
|
||||||
|
pub debug_name: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VirtioGpuCtxCreate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hdr: VirtioGpuCtrlHeader::default(),
|
||||||
|
nlen: 0,
|
||||||
|
context_init: 0,
|
||||||
|
debug_name: [0; 64],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuCtxDestroy {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuCtxResource {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuCmdSubmit {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub size: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuGetCapsetInfo {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub capset_index: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuRespCapsetInfo {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub capset_id: u32,
|
||||||
|
pub capset_max_version: u32,
|
||||||
|
pub capset_max_size: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuGetCapset {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub capset_id: u32,
|
||||||
|
pub capset_version: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VirtioGpuRespCapset {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub capset_data: [u8; 8192],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VirtioGpuRespCapset {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hdr: VirtioGpuCtrlHeader::default(),
|
||||||
|
capset_data: [0u8; 8192],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuResourceMapBlob {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
pub offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuRespMapInfo {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub map_info: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuCmdGetEdid {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub scanout: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuDisplayOne {
|
||||||
|
pub rect: VirtioGpuRect,
|
||||||
|
pub enabled: u32,
|
||||||
|
pub flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VirtioGpuRespDisplayInfo {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VirtioGpuRespDisplayInfo {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hdr: VirtioGpuCtrlHeader::default(),
|
||||||
|
pmodes: [VirtioGpuDisplayOne::default(); VIRTIO_GPU_MAX_SCANOUTS],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VirtioGpuRespEdid {
|
||||||
|
pub hdr: VirtioGpuCtrlHeader,
|
||||||
|
pub size: u32,
|
||||||
|
pub padding: u32,
|
||||||
|
pub edid: [u8; 1024],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VirtioGpuRespEdid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
hdr: VirtioGpuCtrlHeader::default(),
|
||||||
|
size: 0,
|
||||||
|
padding: 0,
|
||||||
|
edid: [0; 1024],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct VirtioGpuConfig {
|
||||||
|
pub events_read: u32,
|
||||||
|
pub events_clear: u32,
|
||||||
|
pub num_scanouts: u32,
|
||||||
|
pub num_capsets: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_of<T>(value: &T) -> &[u8] {
|
||||||
|
let len = size_of::<T>();
|
||||||
|
let ptr = value as *const T as *const u8;
|
||||||
|
// SAFETY: The caller only uses this with #[repr(C)] plain-old-data command structs.
|
||||||
|
unsafe { core::slice::from_raw_parts(ptr, len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_bytes<T>(bytes: &mut Vec<u8>, value: &T) {
|
||||||
|
bytes.extend_from_slice(bytes_of(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_struct<T: Copy>(bytes: &[u8]) -> Result<T> {
|
||||||
|
if bytes.len() < size_of::<T>() {
|
||||||
|
return Err(DriverError::Io(format!(
|
||||||
|
"short VirtIO GPU response: got {} bytes, need {}",
|
||||||
|
bytes.len(),
|
||||||
|
size_of::<T>()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = bytes.as_ptr() as *const T;
|
||||||
|
// SAFETY: We validated the buffer length and use read_unaligned for the DMA byte buffer.
|
||||||
|
Ok(unsafe { ptr.read_unaligned() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_response_type(header: &VirtioGpuCtrlHeader, expected: u32) -> Result<()> {
|
||||||
|
if header.type_ == expected {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(DriverError::Io(format!(
|
||||||
|
"VirtIO GPU response type mismatch: expected {} ({:#06x}), got {} ({:#06x})",
|
||||||
|
response_type_name(expected),
|
||||||
|
expected,
|
||||||
|
response_type_name(header.type_),
|
||||||
|
header.type_
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn response_type_name(type_: u32) -> &'static str {
|
||||||
|
match type_ {
|
||||||
|
VIRTIO_GPU_RESP_OK_NODATA => "OK_NODATA",
|
||||||
|
VIRTIO_GPU_RESP_OK_DISPLAY_INFO => "OK_DISPLAY_INFO",
|
||||||
|
VIRTIO_GPU_RESP_OK_CAPSET_INFO => "OK_CAPSET_INFO",
|
||||||
|
VIRTIO_GPU_RESP_OK_CAPSET => "OK_CAPSET",
|
||||||
|
VIRTIO_GPU_RESP_OK_MAP_INFO => "OK_MAP_INFO",
|
||||||
|
VIRTIO_GPU_RESP_OK_EDID => "OK_EDID",
|
||||||
|
VIRTIO_GPU_RESP_ERR_UNSPEC => "ERR_UNSPEC",
|
||||||
|
VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY => "ERR_OUT_OF_MEMORY",
|
||||||
|
VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID => "ERR_INVALID_SCANOUT_ID",
|
||||||
|
VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID => "ERR_INVALID_RESOURCE_ID",
|
||||||
|
VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID => "ERR_INVALID_CONTEXT_ID",
|
||||||
|
VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER => "ERR_INVALID_PARAMETER",
|
||||||
|
_ => "UNKNOWN",
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,118 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::driver::{DriverError, Result};
|
||||||
|
use crate::gem::GemHandle;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct VirtioResource {
|
||||||
|
pub handle: GemHandle,
|
||||||
|
pub resource_id: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResourceManager {
|
||||||
|
next_resource_id: u32,
|
||||||
|
resources: BTreeMap<GemHandle, VirtioResource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
next_resource_id: 256,
|
||||||
|
resources: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_resource(
|
||||||
|
&mut self,
|
||||||
|
handle: GemHandle,
|
||||||
|
size: u64,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<VirtioResource> {
|
||||||
|
if self.resources.contains_key(&handle) {
|
||||||
|
return Err(DriverError::Buffer(format!(
|
||||||
|
"VirtIO resource already exists for GEM handle {handle}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (res_width, res_height) = if width > 0 && height > 0 {
|
||||||
|
(width, height)
|
||||||
|
} else {
|
||||||
|
let bytes_per_pixel = 4u64;
|
||||||
|
let pixel_count = size.div_ceil(bytes_per_pixel).max(1);
|
||||||
|
let side = (pixel_count as f64).sqrt() as u32;
|
||||||
|
let side = side.max(1);
|
||||||
|
// width * height * 4 must cover the full buffer size
|
||||||
|
let adjusted_height = if side > 0 {
|
||||||
|
u32::try_from(pixel_count.div_ceil(u64::from(side))).unwrap_or(u32::MAX).max(1)
|
||||||
|
} else {
|
||||||
|
u32::try_from(pixel_count).unwrap_or(u32::MAX).max(1)
|
||||||
|
};
|
||||||
|
(side, adjusted_height)
|
||||||
|
};
|
||||||
|
|
||||||
|
let stride = res_width
|
||||||
|
.checked_mul(4)
|
||||||
|
.ok_or_else(|| DriverError::Buffer(format!(
|
||||||
|
"VirtIO resource stride overflow for width {res_width}"
|
||||||
|
)))?;
|
||||||
|
|
||||||
|
let resource = VirtioResource {
|
||||||
|
handle,
|
||||||
|
resource_id: self.next_resource_id,
|
||||||
|
width: res_width,
|
||||||
|
height: res_height,
|
||||||
|
stride,
|
||||||
|
size,
|
||||||
|
};
|
||||||
|
self.next_resource_id = self.next_resource_id.saturating_add(1);
|
||||||
|
self.resources.insert(handle, resource);
|
||||||
|
Ok(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, handle: GemHandle) -> Result<VirtioResource> {
|
||||||
|
self.resources.get(&handle).copied().ok_or_else(|| {
|
||||||
|
DriverError::NotFound(format!("unknown VirtIO resource for GEM handle {handle}"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_resource_id(&mut self, handle: GemHandle) -> Result<u32> {
|
||||||
|
if !self.resources.contains_key(&handle) {
|
||||||
|
return Err(DriverError::NotFound(format!(
|
||||||
|
"unknown VirtIO resource for GEM handle {handle}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let resource_id = self.next_resource_id;
|
||||||
|
self.next_resource_id = self.next_resource_id.saturating_add(1);
|
||||||
|
Ok(resource_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_layout(
|
||||||
|
&mut self,
|
||||||
|
handle: GemHandle,
|
||||||
|
resource_id: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
stride: u32,
|
||||||
|
size: u64,
|
||||||
|
) -> Result<VirtioResource> {
|
||||||
|
let resource = self.resources.get_mut(&handle).ok_or_else(|| {
|
||||||
|
DriverError::NotFound(format!("unknown VirtIO resource for GEM handle {handle}"))
|
||||||
|
})?;
|
||||||
|
resource.resource_id = resource_id;
|
||||||
|
resource.width = width;
|
||||||
|
resource.height = height;
|
||||||
|
resource.stride = stride;
|
||||||
|
resource.size = size;
|
||||||
|
Ok(*resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, handle: GemHandle) -> Option<VirtioResource> {
|
||||||
|
self.resources.remove(&handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
use log::info;
|
||||||
|
use redox_driver_sys::memory::{CacheType, MmioProt, MmioRegion};
|
||||||
|
use redox_driver_sys::pci::{PciDevice, PciDeviceInfo, PCI_CAP_ID_VNDR};
|
||||||
|
|
||||||
|
use crate::driver::{DriverError, Result};
|
||||||
|
|
||||||
|
pub const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
|
||||||
|
pub const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
|
||||||
|
pub const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
|
||||||
|
pub const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;
|
||||||
|
|
||||||
|
const DEVICE_STATUS_ACKNOWLEDGE: u8 = 0x01;
|
||||||
|
const DEVICE_STATUS_DRIVER: u8 = 0x02;
|
||||||
|
const DEVICE_STATUS_DRIVER_OK: u8 = 0x04;
|
||||||
|
const DEVICE_STATUS_FEATURES_OK: u8 = 0x08;
|
||||||
|
const DEVICE_STATUS_FAILED: u8 = 0x80;
|
||||||
|
|
||||||
|
const COMMON_DEVICE_FEATURE_SELECT: usize = 0x00;
|
||||||
|
const COMMON_DEVICE_FEATURE: usize = 0x04;
|
||||||
|
const COMMON_DRIVER_FEATURE_SELECT: usize = 0x08;
|
||||||
|
const COMMON_DRIVER_FEATURE: usize = 0x0C;
|
||||||
|
const COMMON_MSIX_CONFIG: usize = 0x10;
|
||||||
|
const COMMON_NUM_QUEUES: usize = 0x12;
|
||||||
|
const COMMON_DEVICE_STATUS: usize = 0x14;
|
||||||
|
const COMMON_QUEUE_SELECT: usize = 0x16;
|
||||||
|
const COMMON_QUEUE_SIZE: usize = 0x18;
|
||||||
|
const COMMON_QUEUE_MSIX_VECTOR: usize = 0x1A;
|
||||||
|
const COMMON_QUEUE_ENABLE: usize = 0x1C;
|
||||||
|
const COMMON_QUEUE_NOTIFY_OFF: usize = 0x1E;
|
||||||
|
const COMMON_QUEUE_DESC_LO: usize = 0x20;
|
||||||
|
const COMMON_QUEUE_DESC_HI: usize = 0x24;
|
||||||
|
const COMMON_QUEUE_AVAIL_LO: usize = 0x28;
|
||||||
|
const COMMON_QUEUE_AVAIL_HI: usize = 0x2C;
|
||||||
|
const COMMON_QUEUE_USED_LO: usize = 0x30;
|
||||||
|
const COMMON_QUEUE_USED_HI: usize = 0x34;
|
||||||
|
const COMMON_CFG_REQUIRED_BYTES: usize = COMMON_QUEUE_USED_HI + core::mem::size_of::<u32>();
|
||||||
|
|
||||||
|
const ISR_STATUS_OFFSET: usize = 0;
|
||||||
|
const ISR_CFG_REQUIRED_BYTES: usize = ISR_STATUS_OFFSET + core::mem::size_of::<u8>();
|
||||||
|
const DEVICE_CFG_EVENTS_READ: usize = 0;
|
||||||
|
const DEVICE_CFG_EVENTS_CLEAR: usize = 4;
|
||||||
|
const DEVICE_CFG_NUM_SCANOUTS: usize = 8;
|
||||||
|
const DEVICE_CFG_REQUIRED_BYTES: usize = DEVICE_CFG_NUM_SCANOUTS + core::mem::size_of::<u32>();
|
||||||
|
const NOTIFY_CFG_REQUIRED_BYTES: usize = core::mem::size_of::<u16>();
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct VirtioPciCap {
|
||||||
|
cap_vndr: u8,
|
||||||
|
cap_next: u8,
|
||||||
|
cap_len: u8,
|
||||||
|
cfg_type: u8,
|
||||||
|
bar: u8,
|
||||||
|
id: u8,
|
||||||
|
padding: [u8; 2],
|
||||||
|
offset: u32,
|
||||||
|
length: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct VirtioPciNotifyCap {
|
||||||
|
cap: VirtioPciCap,
|
||||||
|
notify_off_multiplier: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QueueConfig {
|
||||||
|
pub index: u16,
|
||||||
|
pub size: u16,
|
||||||
|
pub notify_off: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VirtioModernPciTransport {
|
||||||
|
common_cfg: MmioRegion,
|
||||||
|
notify_cfg: MmioRegion,
|
||||||
|
isr_cfg: MmioRegion,
|
||||||
|
device_cfg: MmioRegion,
|
||||||
|
notify_off_multiplier: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtioModernPciTransport {
|
||||||
|
pub fn new(info: &PciDeviceInfo, pci: &mut PciDevice) -> Result<Self> {
|
||||||
|
let mut common_cap = None;
|
||||||
|
let mut notify_cap = None;
|
||||||
|
let mut isr_cap = None;
|
||||||
|
let mut device_cap = None;
|
||||||
|
|
||||||
|
// Walk the PCI capability linked list directly from config space.
|
||||||
|
// The pcid-spawner handoff provides an empty capabilities vector,
|
||||||
|
// so we cannot rely on info.capabilities.
|
||||||
|
let cap_ptr = pci.read_config_byte(0x34).map_err(pci_error)?;
|
||||||
|
if cap_ptr == 0 {
|
||||||
|
return Err(DriverError::Pci(
|
||||||
|
"VirtIO GPU has no PCI capabilities".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = cap_ptr;
|
||||||
|
let mut visited = 0u8;
|
||||||
|
const MAX_CAPS: u8 = 48;
|
||||||
|
while offset != 0 && visited < MAX_CAPS {
|
||||||
|
visited += 1;
|
||||||
|
let cap_id = pci.read_config_byte(offset as u64).map_err(pci_error)?;
|
||||||
|
let cap_next = pci.read_config_byte(offset as u64 + 1).map_err(pci_error)?;
|
||||||
|
|
||||||
|
if cap_id == PCI_CAP_ID_VNDR {
|
||||||
|
let raw = read_pci_cap(pci, offset)?;
|
||||||
|
match raw.cfg_type {
|
||||||
|
VIRTIO_PCI_CAP_COMMON_CFG => common_cap = Some(raw),
|
||||||
|
VIRTIO_PCI_CAP_NOTIFY_CFG => notify_cap = Some(read_notify_cap(pci, offset)?),
|
||||||
|
VIRTIO_PCI_CAP_ISR_CFG => isr_cap = Some(raw),
|
||||||
|
VIRTIO_PCI_CAP_DEVICE_CFG => device_cap = Some(raw),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = cap_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"redox-drm: VirtIO PCI capability scan found {} caps, common={} notify={} isr={} device={}",
|
||||||
|
visited,
|
||||||
|
common_cap.is_some(),
|
||||||
|
notify_cap.is_some(),
|
||||||
|
isr_cap.is_some(),
|
||||||
|
device_cap.is_some(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let common_cap = common_cap.ok_or_else(|| {
|
||||||
|
DriverError::Pci("VirtIO modern common_cfg capability is missing".into())
|
||||||
|
})?;
|
||||||
|
let notify_cap = notify_cap.ok_or_else(|| {
|
||||||
|
DriverError::Pci("VirtIO modern notify_cfg capability is missing".into())
|
||||||
|
})?;
|
||||||
|
let isr_cap = isr_cap.ok_or_else(|| {
|
||||||
|
DriverError::Pci("VirtIO modern isr_cfg capability is missing".into())
|
||||||
|
})?;
|
||||||
|
let device_cap = device_cap.ok_or_else(|| {
|
||||||
|
DriverError::Pci("VirtIO modern device_cfg capability is missing".into())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let common_cfg =
|
||||||
|
map_cap_region(info, &common_cap, "common_cfg", COMMON_CFG_REQUIRED_BYTES)?;
|
||||||
|
let notify_cfg = map_cap_region(
|
||||||
|
info,
|
||||||
|
¬ify_cap.cap,
|
||||||
|
"notify_cfg",
|
||||||
|
NOTIFY_CFG_REQUIRED_BYTES,
|
||||||
|
)?;
|
||||||
|
let isr_cfg = map_cap_region(info, &isr_cap, "isr_cfg", ISR_CFG_REQUIRED_BYTES)?;
|
||||||
|
let device_cfg =
|
||||||
|
map_cap_region(info, &device_cap, "device_cfg", DEVICE_CFG_REQUIRED_BYTES)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"redox-drm: VirtIO modern PCI transport mapped for {} (notify multiplier {})",
|
||||||
|
info.location, notify_cap.notify_off_multiplier
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
common_cfg,
|
||||||
|
notify_cfg,
|
||||||
|
isr_cfg,
|
||||||
|
device_cfg,
|
||||||
|
notify_off_multiplier: notify_cap.notify_off_multiplier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialize_device(&mut self, requested_features: u64) -> Result<u64> {
|
||||||
|
info!("redox-drm: VirtIO reset device");
|
||||||
|
self.write_device_status(0);
|
||||||
|
|
||||||
|
self.write_device_status(DEVICE_STATUS_ACKNOWLEDGE);
|
||||||
|
self.write_device_status(DEVICE_STATUS_ACKNOWLEDGE | DEVICE_STATUS_DRIVER);
|
||||||
|
|
||||||
|
let available = self.read_device_features();
|
||||||
|
if (available & requested_features) & (1u64 << 32) == 0 {
|
||||||
|
self.fail(format!(
|
||||||
|
"VirtIO GPU missing required VIRTIO_F_VERSION_1 feature (device features={available:#x})"
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let negotiated = available & requested_features;
|
||||||
|
self.write_driver_features(negotiated);
|
||||||
|
|
||||||
|
let mut status = self.device_status();
|
||||||
|
status |= DEVICE_STATUS_FEATURES_OK;
|
||||||
|
self.write_device_status(status);
|
||||||
|
|
||||||
|
if self.device_status() & DEVICE_STATUS_FEATURES_OK == 0 {
|
||||||
|
self.fail("VirtIO GPU rejected FEATURES_OK during negotiation".into())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("redox-drm: VirtIO negotiated features device={available:#x} driver={negotiated:#x}");
|
||||||
|
Ok(negotiated)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finalize_device(&mut self) {
|
||||||
|
let status = self.device_status() | DEVICE_STATUS_DRIVER_OK;
|
||||||
|
self.write_device_status(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_status(&self) -> u8 {
|
||||||
|
self.common_cfg.read8(COMMON_DEVICE_STATUS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_isr_status(&self) -> u8 {
|
||||||
|
self.isr_cfg.read8(ISR_STATUS_OFFSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_events(&self) -> u32 {
|
||||||
|
self.device_cfg.read32(DEVICE_CFG_EVENTS_READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_events(&self, mask: u32) {
|
||||||
|
self.device_cfg.write32(DEVICE_CFG_EVENTS_CLEAR, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_scanouts(&self) -> u32 {
|
||||||
|
self.device_cfg.read32(DEVICE_CFG_NUM_SCANOUTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare_queue(&self, index: u16, requested_size: u16) -> Result<QueueConfig> {
|
||||||
|
self.select_queue(index);
|
||||||
|
let device_size = self.common_cfg.read16(COMMON_QUEUE_SIZE);
|
||||||
|
if device_size == 0 {
|
||||||
|
return Err(DriverError::Initialization(format!(
|
||||||
|
"VirtIO queue {index} reports size 0"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = device_size.min(requested_size);
|
||||||
|
let notify_off = self.common_cfg.read16(COMMON_QUEUE_NOTIFY_OFF);
|
||||||
|
Ok(QueueConfig {
|
||||||
|
index,
|
||||||
|
size,
|
||||||
|
notify_off,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate_queue(
|
||||||
|
&self,
|
||||||
|
index: u16,
|
||||||
|
size: u16,
|
||||||
|
desc_addr: u64,
|
||||||
|
avail_addr: u64,
|
||||||
|
used_addr: u64,
|
||||||
|
msix_vector: Option<u16>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.select_queue(index);
|
||||||
|
self.common_cfg.write16(COMMON_QUEUE_SIZE, size);
|
||||||
|
self.common_cfg
|
||||||
|
.write16(COMMON_QUEUE_MSIX_VECTOR, msix_vector.unwrap_or(u16::MAX));
|
||||||
|
self.write_u64_pair(COMMON_QUEUE_DESC_LO, COMMON_QUEUE_DESC_HI, desc_addr);
|
||||||
|
self.write_u64_pair(COMMON_QUEUE_AVAIL_LO, COMMON_QUEUE_AVAIL_HI, avail_addr);
|
||||||
|
self.write_u64_pair(COMMON_QUEUE_USED_LO, COMMON_QUEUE_USED_HI, used_addr);
|
||||||
|
self.common_cfg.write16(COMMON_QUEUE_ENABLE, 1);
|
||||||
|
|
||||||
|
if self.common_cfg.read16(COMMON_QUEUE_ENABLE) != 1 {
|
||||||
|
return Err(DriverError::Initialization(format!(
|
||||||
|
"VirtIO queue {index} refused queue_enable"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_config_msix_vector(&self, vector: Option<u16>) {
|
||||||
|
self.common_cfg
|
||||||
|
.write16(COMMON_MSIX_CONFIG, vector.unwrap_or(u16::MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify_queue(&self, queue_index: u16, notify_off: u16) -> Result<()> {
|
||||||
|
let byte_offset = usize::from(notify_off)
|
||||||
|
.checked_mul(self.notify_off_multiplier as usize)
|
||||||
|
.ok_or_else(|| DriverError::Mmio("VirtIO notify offset overflow".into()))?;
|
||||||
|
let end = byte_offset
|
||||||
|
.checked_add(core::mem::size_of::<u16>())
|
||||||
|
.ok_or_else(|| DriverError::Mmio("VirtIO notify MMIO overflow".into()))?;
|
||||||
|
if end > self.notify_cfg.size() {
|
||||||
|
return Err(DriverError::Mmio(format!(
|
||||||
|
"VirtIO queue notify outside notify_cfg window: end={end:#x} size={:#x}",
|
||||||
|
self.notify_cfg.size()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.notify_cfg.write16(byte_offset, queue_index);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fail<T>(&mut self, reason: String) -> Result<T> {
|
||||||
|
let status = self.device_status() | DEVICE_STATUS_FAILED;
|
||||||
|
self.write_device_status(status);
|
||||||
|
Err(DriverError::Initialization(reason))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_device_features(&self) -> u64 {
|
||||||
|
self.common_cfg.write32(COMMON_DEVICE_FEATURE_SELECT, 0);
|
||||||
|
let low = self.common_cfg.read32(COMMON_DEVICE_FEATURE) as u64;
|
||||||
|
self.common_cfg.write32(COMMON_DEVICE_FEATURE_SELECT, 1);
|
||||||
|
let high = self.common_cfg.read32(COMMON_DEVICE_FEATURE) as u64;
|
||||||
|
low | (high << 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_driver_features(&self, features: u64) {
|
||||||
|
self.common_cfg.write32(COMMON_DRIVER_FEATURE_SELECT, 0);
|
||||||
|
self.common_cfg
|
||||||
|
.write32(COMMON_DRIVER_FEATURE, features as u32);
|
||||||
|
self.common_cfg.write32(COMMON_DRIVER_FEATURE_SELECT, 1);
|
||||||
|
self.common_cfg
|
||||||
|
.write32(COMMON_DRIVER_FEATURE, (features >> 32) as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_device_status(&self, status: u8) {
|
||||||
|
self.common_cfg.write8(COMMON_DEVICE_STATUS, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_queue(&self, index: u16) {
|
||||||
|
self.common_cfg.write16(COMMON_QUEUE_SELECT, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_u64_pair(&self, lo_offset: usize, hi_offset: usize, value: u64) {
|
||||||
|
self.common_cfg.write32(lo_offset, value as u32);
|
||||||
|
self.common_cfg.write32(hi_offset, (value >> 32) as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_pci_cap(pci: &mut PciDevice, offset: u8) -> Result<VirtioPciCap> {
|
||||||
|
Ok(VirtioPciCap {
|
||||||
|
cap_vndr: pci.read_config_byte(offset as u64).map_err(pci_error)?,
|
||||||
|
cap_next: pci.read_config_byte(offset as u64 + 1).map_err(pci_error)?,
|
||||||
|
cap_len: pci.read_config_byte(offset as u64 + 2).map_err(pci_error)?,
|
||||||
|
cfg_type: pci.read_config_byte(offset as u64 + 3).map_err(pci_error)?,
|
||||||
|
bar: pci.read_config_byte(offset as u64 + 4).map_err(pci_error)?,
|
||||||
|
id: pci.read_config_byte(offset as u64 + 5).map_err(pci_error)?,
|
||||||
|
padding: [
|
||||||
|
pci.read_config_byte(offset as u64 + 6).map_err(pci_error)?,
|
||||||
|
pci.read_config_byte(offset as u64 + 7).map_err(pci_error)?,
|
||||||
|
],
|
||||||
|
offset: pci
|
||||||
|
.read_config_dword(offset as u64 + 8)
|
||||||
|
.map_err(pci_error)?,
|
||||||
|
length: pci
|
||||||
|
.read_config_dword(offset as u64 + 12)
|
||||||
|
.map_err(pci_error)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_notify_cap(pci: &mut PciDevice, offset: u8) -> Result<VirtioPciNotifyCap> {
|
||||||
|
Ok(VirtioPciNotifyCap {
|
||||||
|
cap: read_pci_cap(pci, offset)?,
|
||||||
|
notify_off_multiplier: pci
|
||||||
|
.read_config_dword(offset as u64 + 16)
|
||||||
|
.map_err(pci_error)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pci_error(error: redox_driver_sys::DriverError) -> DriverError {
|
||||||
|
DriverError::Pci(error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_cap_region(
|
||||||
|
info: &PciDeviceInfo,
|
||||||
|
cap: &VirtioPciCap,
|
||||||
|
name: &str,
|
||||||
|
required_size: usize,
|
||||||
|
) -> Result<MmioRegion> {
|
||||||
|
let bar = info
|
||||||
|
.find_memory_bar(cap.bar as usize)
|
||||||
|
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} BAR{} is missing", cap.bar)))?;
|
||||||
|
if cap.length == 0 {
|
||||||
|
return Err(DriverError::Pci(format!(
|
||||||
|
"VirtIO {name} capability has zero length"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if cap.length < required_size as u32 {
|
||||||
|
return Err(DriverError::Pci(format!(
|
||||||
|
"VirtIO {name} capability too small: {} < {} bytes",
|
||||||
|
cap.length, required_size
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cap_end = u64::from(cap.offset)
|
||||||
|
.checked_add(u64::from(cap.length))
|
||||||
|
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} capability range overflow")))?;
|
||||||
|
if cap_end > bar.size {
|
||||||
|
return Err(DriverError::Pci(format!(
|
||||||
|
"VirtIO {name} capability range [{:#x}, {:#x}) exceeds BAR{} size {:#x}",
|
||||||
|
cap.offset, cap_end, cap.bar, bar.size
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let phys = bar
|
||||||
|
.addr
|
||||||
|
.checked_add(cap.offset as u64)
|
||||||
|
.ok_or_else(|| DriverError::Pci(format!("VirtIO {name} MMIO address overflow")))?;
|
||||||
|
MmioRegion::map(
|
||||||
|
phys,
|
||||||
|
cap.length as usize,
|
||||||
|
CacheType::DeviceMemory,
|
||||||
|
MmioProt::READ_WRITE,
|
||||||
|
)
|
||||||
|
.map_err(|e| DriverError::Mmio(format!("failed to map VirtIO {name}: {e}")))
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::sync::atomic::{fence, Ordering};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use redox_driver_sys::dma::DmaBuffer;
|
||||||
|
|
||||||
|
use crate::driver::{DriverError, Result};
|
||||||
|
|
||||||
|
use super::transport::VirtioModernPciTransport;
|
||||||
|
|
||||||
|
const VIRTQ_DESC_F_NEXT: u16 = 1;
|
||||||
|
const VIRTQ_DESC_F_WRITE: u16 = 2;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct VirtqDesc {
|
||||||
|
addr: u64,
|
||||||
|
len: u32,
|
||||||
|
flags: u16,
|
||||||
|
next: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
struct VirtqUsedElem {
|
||||||
|
id: u32,
|
||||||
|
len: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Virtqueue {
|
||||||
|
index: u16,
|
||||||
|
size: u16,
|
||||||
|
notify_off: u16,
|
||||||
|
desc: DmaBuffer,
|
||||||
|
avail: DmaBuffer,
|
||||||
|
used: DmaBuffer,
|
||||||
|
free_list: Vec<u16>,
|
||||||
|
pending: BTreeMap<u16, Vec<u16>>,
|
||||||
|
last_used_idx: u16,
|
||||||
|
failed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Virtqueue {
|
||||||
|
pub fn new(index: u16, size: u16, notify_off: u16) -> Result<Self> {
|
||||||
|
let desc_bytes = usize::from(size) * core::mem::size_of::<VirtqDesc>();
|
||||||
|
let avail_bytes = 6 + usize::from(size) * 2;
|
||||||
|
let used_bytes = 6 + usize::from(size) * core::mem::size_of::<VirtqUsedElem>();
|
||||||
|
|
||||||
|
let desc = DmaBuffer::allocate(desc_bytes, 16)
|
||||||
|
.map_err(|e| DriverError::Buffer(format!("VirtIO desc allocation failed: {e}")))?;
|
||||||
|
let avail = DmaBuffer::allocate(avail_bytes, 2)
|
||||||
|
.map_err(|e| DriverError::Buffer(format!("VirtIO avail allocation failed: {e}")))?;
|
||||||
|
let used = DmaBuffer::allocate(used_bytes, 4)
|
||||||
|
.map_err(|e| DriverError::Buffer(format!("VirtIO used allocation failed: {e}")))?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
index,
|
||||||
|
size,
|
||||||
|
notify_off,
|
||||||
|
desc,
|
||||||
|
avail,
|
||||||
|
used,
|
||||||
|
free_list: (0..size).rev().collect(),
|
||||||
|
pending: BTreeMap::new(),
|
||||||
|
last_used_idx: 0,
|
||||||
|
failed: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self) -> u16 {
|
||||||
|
self.index
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> u16 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify_off(&self) -> u16 {
|
||||||
|
self.notify_off
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn desc_addr(&self) -> u64 {
|
||||||
|
self.desc.physical_address() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn avail_addr(&self) -> u64 {
|
||||||
|
self.avail.physical_address() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn used_addr(&self) -> u64 {
|
||||||
|
self.used.physical_address() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submit_request(
|
||||||
|
&mut self,
|
||||||
|
transport: &VirtioModernPciTransport,
|
||||||
|
request_addr: u64,
|
||||||
|
request_len: u32,
|
||||||
|
response_addr: u64,
|
||||||
|
response_len: u32,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Result<()> {
|
||||||
|
if self.failed {
|
||||||
|
return Err(DriverError::Io(format!(
|
||||||
|
"VirtIO queue {} is failed and cannot accept more requests",
|
||||||
|
self.index
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let head = self.alloc_desc()?;
|
||||||
|
let tail = self.alloc_desc()?;
|
||||||
|
self.write_desc(
|
||||||
|
head,
|
||||||
|
VirtqDesc {
|
||||||
|
addr: request_addr,
|
||||||
|
len: request_len,
|
||||||
|
flags: VIRTQ_DESC_F_NEXT,
|
||||||
|
next: tail,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.write_desc(
|
||||||
|
tail,
|
||||||
|
VirtqDesc {
|
||||||
|
addr: response_addr,
|
||||||
|
len: response_len,
|
||||||
|
flags: VIRTQ_DESC_F_WRITE,
|
||||||
|
next: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
self.pending.insert(head, vec![head, tail]);
|
||||||
|
|
||||||
|
// VirtIO spec 2.6.8.1: The driver MUST perform a memory barrier before
|
||||||
|
// updating the available index to ensure the descriptor table writes and
|
||||||
|
// the available ring entry are visible to the device.
|
||||||
|
fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
self.push_avail(head);
|
||||||
|
if let Err(error) = transport.notify_queue(self.index, self.notify_off) {
|
||||||
|
self.reclaim_pending_chain(head);
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(error) = self.wait_used(head, timeout) {
|
||||||
|
self.failed = true;
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_desc(&mut self) -> Result<u16> {
|
||||||
|
self.free_list.pop().ok_or_else(|| {
|
||||||
|
DriverError::Buffer(format!(
|
||||||
|
"VirtIO queue {} ran out of descriptors",
|
||||||
|
self.index
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_avail(&mut self, head: u16) {
|
||||||
|
let avail_idx = self.read_avail_idx();
|
||||||
|
let slot = usize::from(avail_idx % self.size);
|
||||||
|
self.write_avail_ring(slot, head);
|
||||||
|
fence(Ordering::Release);
|
||||||
|
self.write_avail_idx(avail_idx.wrapping_add(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_used(&mut self, expected_head: u16, timeout: Duration) -> Result<()> {
|
||||||
|
let deadline = Instant::now() + timeout;
|
||||||
|
loop {
|
||||||
|
let used_idx = self.read_used_idx();
|
||||||
|
if used_idx != self.last_used_idx {
|
||||||
|
let slot = usize::from(self.last_used_idx % self.size);
|
||||||
|
let elem = self.read_used_elem(slot);
|
||||||
|
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||||
|
self.free_chain(elem.id as u16)?;
|
||||||
|
if elem.id as u16 != expected_head {
|
||||||
|
self.failed = true;
|
||||||
|
return Err(DriverError::Io(format!(
|
||||||
|
"VirtIO queue {} completed descriptor head {} while waiting for {}",
|
||||||
|
self.index, elem.id, expected_head
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if Instant::now() >= deadline {
|
||||||
|
return Err(DriverError::Io(format!(
|
||||||
|
"VirtIO queue {} timed out waiting for descriptor head {}",
|
||||||
|
self.index, expected_head
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::yield_now();
|
||||||
|
thread::sleep(Duration::from_millis(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_chain(&mut self, head: u16) -> Result<()> {
|
||||||
|
let chain = self.pending.remove(&head).ok_or_else(|| {
|
||||||
|
DriverError::Io(format!(
|
||||||
|
"VirtIO queue {} completed unknown descriptor head {}",
|
||||||
|
self.index, head
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
for index in chain {
|
||||||
|
self.free_list.push(index);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reclaim_pending_chain(&mut self, head: u16) {
|
||||||
|
if let Some(chain) = self.pending.remove(&head) {
|
||||||
|
for index in chain {
|
||||||
|
self.free_list.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_desc(&mut self, index: u16, desc: VirtqDesc) {
|
||||||
|
let ptr = self.desc.as_mut_ptr() as *mut VirtqDesc;
|
||||||
|
// SAFETY: The DMA buffer is sized for `size` descriptors, and callers only pass indices < size.
|
||||||
|
unsafe { ptr.add(index as usize).write(desc) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_used_elem(&self, slot: usize) -> VirtqUsedElem {
|
||||||
|
let offset = 4 + slot * core::mem::size_of::<VirtqUsedElem>();
|
||||||
|
let ptr = self.used.as_ptr().wrapping_add(offset) as *const VirtqUsedElem;
|
||||||
|
// SAFETY: The used ring allocation covers all ring entries; read_unaligned handles packed access.
|
||||||
|
unsafe { ptr.read_unaligned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_avail_idx(&self) -> u16 {
|
||||||
|
let ptr = self.avail.as_ptr().wrapping_add(2) as *const u16;
|
||||||
|
// SAFETY: Offset 2 is the avail.idx field in the DMA allocation.
|
||||||
|
unsafe { ptr.read_unaligned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_avail_idx(&mut self, value: u16) {
|
||||||
|
let ptr = self.avail.as_mut_ptr().wrapping_add(2) as *mut u16;
|
||||||
|
// SAFETY: Offset 2 is the avail.idx field in the DMA allocation.
|
||||||
|
unsafe { ptr.write_unaligned(value) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_avail_ring(&mut self, slot: usize, value: u16) {
|
||||||
|
let offset = 4 + slot * core::mem::size_of::<u16>();
|
||||||
|
let ptr = self.avail.as_mut_ptr().wrapping_add(offset) as *mut u16;
|
||||||
|
// SAFETY: The avail ring allocation is sized for `size` u16 entries.
|
||||||
|
unsafe { ptr.write_unaligned(value) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_used_idx(&self) -> u16 {
|
||||||
|
let ptr = self.used.as_ptr().wrapping_add(2) as *const u16;
|
||||||
|
// SAFETY: Offset 2 is the used.idx field in the DMA allocation.
|
||||||
|
unsafe { ptr.read_unaligned() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,7 +91,7 @@ fn run(daemon: daemon::Daemon) -> Result<()> {
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(16));
|
std::thread::sleep(std::time::Duration::from_millis(16));
|
||||||
});
|
});
|
||||||
|
|
||||||
let drm_scheme = Arc::new(Mutex::new(DrmScheme::new(driver)));
|
let drm_scheme = Arc::new(Mutex::new(DrmScheme::new(driver, info)));
|
||||||
let event_scheme = drm_scheme.clone();
|
let event_scheme = drm_scheme.clone();
|
||||||
|
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || loop {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@ add_subdirectory(src)
|
|||||||
|
|
||||||
# Enable unit testing
|
# Enable unit testing
|
||||||
if (BUILD_TESTING)
|
if (BUILD_TESTING)
|
||||||
################################# add_subdirectory(autotests)
|
################################################### add_subdirectory(autotests)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
# shall we use DBus?
|
# shall we use DBus?
|
||||||
# enabled per default on Linux & BSD systems
|
# enabled per default on Linux & BSD systems
|
||||||
|
|||||||
@@ -95,6 +95,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED)
|
find_package(KF6Codecs ${KF_DEP_VERSION} REQUIRED)
|
||||||
find_package(KF6Config ${KF_DEP_VERSION} REQUIRED)
|
find_package(KF6Config ${KF_DEP_VERSION} REQUIRED)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ cmake "${COOKBOOK_SOURCE}" \
|
|||||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
-DBUILD_TESTING=OFF \
|
-DBUILD_TESTING=OFF \
|
||||||
-DBUILD_QCH=OFF \
|
-DBUILD_QCH=OFF \
|
||||||
-DKCONFIG_USE_QML=OFF \
|
-DKCONFIG_USE_QML=ON \
|
||||||
-DUSE_DBUS=OFF \
|
-DUSE_DBUS=OFF \
|
||||||
-Wno-dev
|
-Wno-dev
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
# shall we use DBus?
|
# shall we use DBus?
|
||||||
# enabled per default on Linux & BSD systems
|
# enabled per default on Linux & BSD systems
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ find_package(KF6GuiAddons ${KF_DEP_VERSION} REQUIRED)
|
|||||||
|
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX)
|
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT REDOX)
|
||||||
######################################################## find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
|
########################################################################## find_package(KF6GlobalAccel ${KF_DEP_VERSION} REQUIRED)
|
||||||
set(HAVE_KGLOBALACCEL TRUE)
|
set(HAVE_KGLOBALACCEL TRUE)
|
||||||
else()
|
else()
|
||||||
set(HAVE_KGLOBALACCEL FALSE)
|
set(HAVE_KGLOBALACCEL FALSE)
|
||||||
|
|||||||
@@ -108,6 +108,25 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
find_package(Qt6Svg ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||||
|
|
||||||
# shall we use DBus?
|
# shall we use DBus?
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#TODO: KIdleTime — idle time detection. Used by KWin for screen dimming/locking.
|
# KIdleTime — idle time detection. Used by KWin for screen dimming/locking.
|
||||||
[source]
|
[source]
|
||||||
tar = "https://invent.kde.org/frameworks/kidletime/-/archive/v6.10.0/kidletime-v6.10.0.tar.gz"
|
tar = "https://invent.kde.org/frameworks/kidletime/-/archive/v6.10.0/kidletime-v6.10.0.tar.gz"
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ set_package_properties(Qt6Qml PROPERTIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (TARGET Qt6::Qml)
|
if (TARGET Qt6::Qml)
|
||||||
################################### include(ECMQmlModule)
|
##################################################### include(ECMQmlModule)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
if (TARGET Qt6::Qml)
|
if (TARGET Qt6::Qml)
|
||||||
################################## add_subdirectory(qml)
|
#################################################### add_subdirectory(qml)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ecm_qt_install_logging_categories(
|
ecm_qt_install_logging_categories(
|
||||||
|
|||||||
@@ -73,6 +73,25 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
|
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID AND NOT HAIKU)
|
||||||
option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON)
|
option(WITH_X11 "Build with support for QX11Info::appUserTime()" ON)
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#TODO: KSvg — SVG rendering library with theme re-coloring and disk caching
|
||||||
|
[source]
|
||||||
|
tar = "https://invent.kde.org/frameworks/ksvg/-/archive/v6.10.0/ksvg-v6.10.0.tar.gz"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
dependencies = [
|
||||||
|
"qtbase",
|
||||||
|
"kf6-extra-cmake-modules",
|
||||||
|
"kf6-karchive",
|
||||||
|
"kf6-kconfig",
|
||||||
|
"kf6-kcolorscheme",
|
||||||
|
"kf6-kcoreaddons",
|
||||||
|
"kf6-kguiaddons",
|
||||||
|
]
|
||||||
|
script = """
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
||||||
|
source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh"
|
||||||
|
|
||||||
|
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
||||||
|
|
||||||
|
sed -i "s/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/" \
|
||||||
|
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||||
|
sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' \
|
||||||
|
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Disable QML/Quick declarative imports — not needed without Qt6Quick
|
||||||
|
sed -i 's/^include(ECMGenerateQmlTypes)/#include(ECMGenerateQmlTypes)/' \
|
||||||
|
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||||
|
sed -i 's/^include(ECMQmlModule)/#include(ECMQmlModule)/' \
|
||||||
|
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null || true
|
||||||
|
|
||||||
|
rm -f CMakeCache.txt
|
||||||
|
rm -rf CMakeFiles
|
||||||
|
|
||||||
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
|
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
|
-DBUILD_TESTING=OFF \
|
||||||
|
-DBUILD_QCH=OFF \
|
||||||
|
-DBUILD_TOOLS=OFF \
|
||||||
|
-DUSE_DBUS=OFF \
|
||||||
|
-Wno-dev
|
||||||
|
|
||||||
|
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||||
|
cmake --install . --prefix "${COOKBOOK_STAGE}/usr"
|
||||||
|
|
||||||
|
for lib in "${COOKBOOK_STAGE}/usr/lib/"libKF6*.so.*; do
|
||||||
|
[ -f "${lib}" ] || continue
|
||||||
|
patchelf --remove-rpath "${lib}" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
"""
|
||||||
@@ -24,10 +24,10 @@ include(ECMSetupVersion)
|
|||||||
include(ECMQtDeclareLoggingCategory)
|
include(ECMQtDeclareLoggingCategory)
|
||||||
include(ECMAddQch)
|
include(ECMAddQch)
|
||||||
include(KDEPackageAppTemplates)
|
include(KDEPackageAppTemplates)
|
||||||
include(ECMGenerateQmlTypes)
|
#include(ECMGenerateQmlTypes)
|
||||||
include(ECMMarkNonGuiExecutable)
|
include(ECMMarkNonGuiExecutable)
|
||||||
include(ECMDeprecationSettings)
|
include(ECMDeprecationSettings)
|
||||||
include(ECMQmlModule)
|
#include(ECMQmlModule)
|
||||||
|
|
||||||
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
|
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
|
||||||
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
|
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
|
||||||
|
|||||||
@@ -90,6 +90,24 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
|||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED)
|
||||||
|
|
||||||
if (WITH_TEXT_TO_SPEECH)
|
if (WITH_TEXT_TO_SPEECH)
|
||||||
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech)
|
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG REQUIRED TextToSpeech)
|
||||||
|
|||||||
@@ -93,6 +93,25 @@ find_package(Qt6WaylandClientPrivate REQUIRED)
|
|||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
find_package(Qt6WaylandClientPrivate REQUIRED)
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
|
find_package(Qt6WaylandClientPrivate REQUIRED)
|
||||||
set_package_properties(Wayland PROPERTIES
|
set_package_properties(Wayland PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ void initializeLanguages()
|
|||||||
// Ideally setting the LANGUAGE would change the default QLocale too
|
// Ideally setting the LANGUAGE would change the default QLocale too
|
||||||
// but unfortunately this is too late since the QCoreApplication constructor
|
// but unfortunately this is too late since the QCoreApplication constructor
|
||||||
// already created a QLocale at this stage so we need to set the reset it
|
// already created a QLocale at this stage so we need to set the reset it
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // by triggering the creation and destruction of a QSystemLocale
|
||||||
// this is highly dependent on Qt internals, so may break, but oh well
|
// this is highly dependent on Qt internals, so may break, but oh well
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// QSystemLocale *dummy = new QSystemLocale();
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// delete dummy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ ecm_set_disabled_deprecation_versions(
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory( src )
|
add_subdirectory( src )
|
||||||
######if (BUILD_TESTING)
|
########################if (BUILD_TESTING)
|
||||||
###### add_subdirectory( autotests )
|
######################## add_subdirectory( autotests )
|
||||||
######endif()
|
########################endif()
|
||||||
|
|
||||||
if (BUILD_QCH)
|
if (BUILD_QCH)
|
||||||
ecm_install_qch_export(
|
ecm_install_qch_export(
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ set_package_properties(PList PROPERTIES
|
|||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
if (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||||
# Used by the UDisks backend on Linux
|
# Used by the UDisks backend on Linux
|
||||||
#########################################################################find_package(LibMount)
|
###########################################################################################find_package(LibMount)
|
||||||
set_package_properties(LibMount PROPERTIES
|
set_package_properties(LibMount PROPERTIES
|
||||||
TYPE REQUIRED)
|
TYPE REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
#TODO: KWin — Qt6::Sensors now available (2026-04-30). Remaining blockers:
|
#TODO: KWin — full build with Qt6Quick/QML. Effect frames, scripted effects, OSD overlay, outline overlay all enabled.
|
||||||
# QML/Quick (JIT disabled on Redox) prevents full KWin build.
|
|
||||||
# Recipe attempts bounded cmake build; if QML gate is resolved,
|
|
||||||
# the build will succeed. Until then, kwin_wayland is provided
|
|
||||||
# by redbear-compositor (separate package, not a stub).
|
|
||||||
[source]
|
[source]
|
||||||
tar = "https://invent.kde.org/plasma/kwin/-/archive/v6.3.4/kwin-v6.3.4.tar.gz"
|
tar = "https://invent.kde.org/plasma/kwin/-/archive/v6.3.4/kwin-v6.3.4.tar.gz"
|
||||||
blake3 = "2aa1e234a75b0aa94f0da3a74d93e2a8e49b30a3afb12dc24b2ecd3abaa94e7f"
|
blake3 = "2aa1e234a75b0aa94f0da3a74d93e2a8e49b30a3afb12dc24b2ecd3abaa94e7f"
|
||||||
@@ -13,30 +9,47 @@ dependencies = [
|
|||||||
"qtbase",
|
"qtbase",
|
||||||
"qtdeclarative",
|
"qtdeclarative",
|
||||||
"qt5compat",
|
"qt5compat",
|
||||||
"qt6-sensors",
|
|
||||||
"kf6-extra-cmake-modules",
|
"kf6-extra-cmake-modules",
|
||||||
"kf6-kcoreaddons",
|
"kf6-kauth",
|
||||||
"kf6-ki18n",
|
"kf6-kcolorscheme",
|
||||||
|
"kf6-kcompletion",
|
||||||
"kf6-kconfig",
|
"kf6-kconfig",
|
||||||
|
"kf6-kconfigwidgets",
|
||||||
|
"kf6-kcoreaddons",
|
||||||
"kf6-kcrash",
|
"kf6-kcrash",
|
||||||
"kf6-kdbusaddons",
|
"kf6-kdbusaddons",
|
||||||
"kf6-kservice",
|
|
||||||
"kf6-kwayland",
|
|
||||||
"kf6-kwindowsystem",
|
|
||||||
"kf6-kconfigwidgets",
|
|
||||||
"kf6-kglobalaccel",
|
"kf6-kglobalaccel",
|
||||||
|
"kf6-kguiaddons",
|
||||||
|
"kf6-ki18n",
|
||||||
"kf6-kidletime",
|
"kf6-kidletime",
|
||||||
"kf6-knotifications",
|
|
||||||
"kf6-kpackage",
|
"kf6-kpackage",
|
||||||
"kf6-kdeclarative",
|
"kf6-kservice",
|
||||||
"kf6-kio",
|
"kf6-ksvg",
|
||||||
"kdecoration",
|
"kf6-kwayland",
|
||||||
|
"kf6-kwidgetsaddons",
|
||||||
|
"kf6-kwindowsystem",
|
||||||
|
"kf6-kxmlgui",
|
||||||
|
"kf6-kiconthemes",
|
||||||
"kf6-kcmutils",
|
"kf6-kcmutils",
|
||||||
|
"kf6-kdeclarative",
|
||||||
|
"kf6-knotifications",
|
||||||
|
"kf6-kio",
|
||||||
|
"kf6-kitemmodels",
|
||||||
|
"kf6-kitemviews",
|
||||||
|
"kf6-kjobwidgets",
|
||||||
|
"kf6-ktextwidgets",
|
||||||
|
"kdecoration",
|
||||||
"plasma-wayland-protocols",
|
"plasma-wayland-protocols",
|
||||||
"libepoxy-stub",
|
"libepoxy",
|
||||||
"libudev-stub",
|
"libdisplay-info",
|
||||||
|
"libxcvt",
|
||||||
|
"lcms2",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"redbear-compositor",
|
"redbear-compositor",
|
||||||
|
"libxkbcommon",
|
||||||
|
"libinput",
|
||||||
|
"libudev-stub",
|
||||||
|
"libxcb",
|
||||||
]
|
]
|
||||||
script = """
|
script = """
|
||||||
DYNAMIC_INIT
|
DYNAMIC_INIT
|
||||||
@@ -44,84 +57,128 @@ DYNAMIC_INIT
|
|||||||
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
HOST_BUILD="${COOKBOOK_ROOT}/build/qt-host-build"
|
||||||
STAGE="${COOKBOOK_STAGE}/usr"
|
STAGE="${COOKBOOK_STAGE}/usr"
|
||||||
|
|
||||||
for qtdir in plugins mkspecs metatypes modules; do
|
source "${COOKBOOK_ROOT}/local/scripts/lib/qt-sysroot.sh"
|
||||||
if [ -d "${COOKBOOK_SYSROOT}/usr/${qtdir}" ] && [ ! -e "${COOKBOOK_SYSROOT}/${qtdir}" ]; then
|
redbear_qt_link_sysroot_dirs "${COOKBOOK_SYSROOT}" plugins mkspecs metatypes modules
|
||||||
ln -s "usr/${qtdir}" "${COOKBOOK_SYSROOT}/${qtdir}"
|
|
||||||
|
if [ -d "${COOKBOOK_SYSROOT}/plugins" ] && [ ! -L "${COOKBOOK_SYSROOT}/plugins" ]; then
|
||||||
|
if [ -d "${COOKBOOK_SYSROOT}/usr/plugins" ]; then
|
||||||
|
cp -an "${COOKBOOK_SYSROOT}/plugins/." "${COOKBOOK_SYSROOT}/usr/plugins/" 2>/dev/null || true
|
||||||
|
rm -rf "${COOKBOOK_SYSROOT}/plugins"
|
||||||
|
ln -s usr/plugins "${COOKBOOK_SYSROOT}/plugins"
|
||||||
fi
|
fi
|
||||||
done
|
fi
|
||||||
|
|
||||||
# Attempt real cmake build with Redox-viable feature set
|
cp -a "${COOKBOOK_ROOT}/recipes/libs/freetype2/target/x86_64-unknown-redox/stage/usr/." "${COOKBOOK_SYSROOT}/" 2>/dev/null || true
|
||||||
sed -i "s/^ecm_install_po_files_as_qm/#ecm_install_po_files_as_qm/" \
|
cp -a "${COOKBOOK_ROOT}/recipes/dev/fontconfig/target/x86_64-unknown-redox/stage/usr/." "${COOKBOOK_SYSROOT}/" 2>/dev/null || true
|
||||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
cp -rn "${HOST_BUILD}/include/QtGui/6.11.0/QtGui/private/." "${COOKBOOK_SYSROOT}/include/QtGui/6.11.0/QtGui/private/" 2>/dev/null || true
|
||||||
sed -i 's/^ki18n_install(po)/#ki18n_install(po)/' \
|
|
||||||
"${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
|
||||||
sed -i '/include(ECMQmlModule)/s/^/#/' "${COOKBOOK_SOURCE}/CMakeLists.txt" 2>/dev/null
|
|
||||||
|
|
||||||
# Remove Qt components not available in cross-build
|
FT2_DIR="${COOKBOOK_ROOT}/recipes/libs/freetype2/target/x86_64-unknown-redox/stage/usr"
|
||||||
sed -i '/^ UiTools$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
FC_DIR="${COOKBOOK_ROOT}/recipes/dev/fontconfig/target/x86_64-unknown-redox/stage/usr"
|
||||||
sed -i '/^ Sensors$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
CROSS_PKGCONFIG="${COOKBOOK_ROOT}/bin/x86_64-unknown-redox-pkg-config"
|
||||||
|
|
||||||
# Remove KF6::Svg references (not available in cross-build)
|
sed -i 's/Canberra::Canberra/$<IF:$<BOOL:${Canberra_FOUND}>,Canberra::Canberra,>/' \
|
||||||
find "${COOKBOOK_SOURCE}" -name "CMakeLists.txt" -exec sed -i '/KF6::Svg/d' {} +
|
"${COOKBOOK_SOURCE}/src/plugins/systembell/CMakeLists.txt" 2>/dev/null || true
|
||||||
|
|
||||||
|
sed -i '/^[[:space:]]*UiTools$/d' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||||
|
sed -i 's/find_package(Canberra REQUIRED)/find_package(Canberra QUIET)/' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||||
|
sed -i 's/add_subdirectory(killer)/#add_subdirectory(killer) # disabled: X11-only/' "${COOKBOOK_SOURCE}/src/helpers/CMakeLists.txt"
|
||||||
|
|
||||||
|
printf '%s\n' '#ifndef SUN_LEN' '#define SUN_LEN(s) (sizeof(*(s)) - sizeof((s)->sun_path) + strnlen((s)->sun_path, sizeof((s)->sun_path)))' '#endif' | cat - "${COOKBOOK_SOURCE}/src/helpers/wayland_wrapper/wl-socket.c" > /tmp/wlsocket.tmp && mv /tmp/wlsocket.tmp "${COOKBOOK_SOURCE}/src/helpers/wayland_wrapper/wl-socket.c"
|
||||||
|
|
||||||
|
python3 -c "
|
||||||
|
path = '$COOKBOOK_SOURCE/src/cursor.cpp'
|
||||||
|
with open(path) as f: t = f.read()
|
||||||
|
t = t.replace('#include <xcb/xcb_cursor.h>', '')
|
||||||
|
with open(path, 'w') as f: f.write(t)
|
||||||
|
"
|
||||||
|
|
||||||
|
sed -i 's/message(FATAL_ERROR "Compiler does not support C++20 ranges")/message(WARNING "C++20 ranges test skipped — strtold toolchain issue")/' "${COOKBOOK_SOURCE}/CMakeLists.txt"
|
||||||
|
|
||||||
|
sed -i '/#include "tabletmodemanager.h"/a #include <libudev.h>' \
|
||||||
|
"${COOKBOOK_SOURCE}/src/tabletmodemanager.cpp" 2>/dev/null || true
|
||||||
|
|
||||||
|
sed -i '/#include "device.h"/a #include <libudev.h>' \
|
||||||
|
"${COOKBOOK_SOURCE}/src/backends/libinput/device.cpp" 2>/dev/null || true
|
||||||
|
|
||||||
|
sed -i 's/accept4(socketDescriptor, nullptr, nullptr, SOCK_CLOEXEC)/accept(socketDescriptor, nullptr, nullptr)/' \
|
||||||
|
"${COOKBOOK_SOURCE}/src/wayland/display.cpp" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo '#ifndef F_ADD_SEALS
|
||||||
|
#define F_ADD_SEALS 1033
|
||||||
|
#define F_GET_SEALS 1034
|
||||||
|
#define F_SEAL_SEAL 0x0001
|
||||||
|
#define F_SEAL_SHRINK 0x0002
|
||||||
|
#define F_SEAL_GROW 0x0004
|
||||||
|
#define F_SEAL_WRITE 0x0008
|
||||||
|
#endif' >> "${COOKBOOK_SYSROOT}/include/fcntl.h"
|
||||||
|
|
||||||
|
printf '%s\n' '#define F_ADD_SEALS 1033' '#define F_GET_SEALS 1034' '#define F_SEAL_SEAL 0x0001' '#define F_SEAL_SHRINK 0x0002' '#define F_SEAL_GROW 0x0004' '#define F_SEAL_WRITE 0x0008' | cat - "${COOKBOOK_SOURCE}/src/utils/ramfile.cpp" > /tmp/ramfile.tmp && mv /tmp/ramfile.tmp "${COOKBOOK_SOURCE}/src/utils/ramfile.cpp"
|
||||||
|
|
||||||
rm -f CMakeCache.txt
|
rm -f CMakeCache.txt
|
||||||
rm -rf CMakeFiles
|
rm -rf CMakeFiles
|
||||||
|
|
||||||
# Qt cmake Imported targets check plugins/ for .so files, but qtwayland and other
|
rm -rf "${COOKBOOK_SOURCE}/po"
|
||||||
# modules install them under usr/plugins/. Mirror any missing files across.
|
|
||||||
# Also remove QmlPlugins dirs which reference host paths from QML module.
|
|
||||||
find "${COOKBOOK_SYSROOT}" -type d -name "QmlPlugins" -exec rm -rf {} + 2>/dev/null || true
|
|
||||||
if [ -d "${COOKBOOK_SYSROOT}/usr/plugins" ] && [ -d "${COOKBOOK_SYSROOT}/plugins" ]; then
|
|
||||||
for src in $(find "${COOKBOOK_SYSROOT}/usr/plugins" -name "*.so"); do
|
|
||||||
dst="${COOKBOOK_SYSROOT}/plugins/${src#${COOKBOOK_SYSROOT}/usr/plugins/}"
|
|
||||||
if [ ! -e "$dst" ]; then
|
|
||||||
mkdir -p "$(dirname "$dst")"
|
|
||||||
ln -sf "$src" "$dst" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Stub missing KF6 packages needed by dependencies
|
HOST_SCANNER="${COOKBOOK_ROOT}/local/recipes/kde/kwin/target/qtwaylandscanner-host/qtwaylandscanner_kde"
|
||||||
mkdir -p "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg"
|
|
||||||
cat > "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg/KF6SvgConfig.cmake" << 'KF6EOF'
|
|
||||||
set(KF6Svg_FOUND TRUE)
|
|
||||||
KF6EOF
|
|
||||||
|
|
||||||
# Stub missing KF6 packages needed by dependencies
|
HOST_SCANNER_BUILD="${COOKBOOK_ROOT}/local/recipes/kde/kwin/target/qtwaylandscanner-host"
|
||||||
mkdir -p "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg"
|
mkdir -p "${HOST_SCANNER_BUILD}"
|
||||||
cat > "${COOKBOOK_SYSROOT}/lib/cmake/KF6Svg/KF6SvgConfig.cmake" << 'KF6EOF'
|
env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR \
|
||||||
set(KF6Svg_FOUND TRUE)
|
/usr/bin/pkg-config --exists Qt6Core 2>/dev/null && \
|
||||||
KF6EOF
|
env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR \
|
||||||
|
g++ -std=c++20 -fPIC -o "${HOST_SCANNER_BUILD}/qtwaylandscanner_kde" \
|
||||||
|
"${COOKBOOK_SOURCE}/src/wayland/tools/qtwaylandscanner.cpp" \
|
||||||
|
$(env -u LDFLAGS -u LIBRARY_PATH -u PKG_CONFIG_PATH -u PKG_CONFIG_LIBDIR /usr/bin/pkg-config --cflags --libs Qt6Core) || \
|
||||||
|
env -u LDFLAGS -u LIBRARY_PATH g++ -std=c++20 -fPIC -o "${HOST_SCANNER_BUILD}/qtwaylandscanner_kde" \
|
||||||
|
"${COOKBOOK_SOURCE}/src/wayland/tools/qtwaylandscanner.cpp" \
|
||||||
|
-I"${HOST_BUILD}/include" -I"${HOST_BUILD}/include/QtCore" \
|
||||||
|
-DQT_CORE_LIB -L"${HOST_BUILD}/lib" -lQt6Core -Wl,-rpath,"${HOST_BUILD}/lib"
|
||||||
|
|
||||||
|
cp -f /usr/bin/wayland-scanner "${COOKBOOK_SYSROOT}/bin/wayland-scanner"
|
||||||
|
|
||||||
|
grep -q "strtold" "${COOKBOOK_SYSROOT}/include/stdlib.h" 2>/dev/null || sed -i '/^#endif/ i long double strtold(const char *nptr, char **endptr);' "${COOKBOOK_SYSROOT}/include/stdlib.h"
|
||||||
|
|
||||||
cmake "${COOKBOOK_SOURCE}" \
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||||
|
-DKF6_HOST_TOOLING="${HOST_BUILD}/lib/cmake" \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
|
-DPKG_CONFIG_EXECUTABLE="${CROSS_PKGCONFIG}" \
|
||||||
|
-DFREETYPE_LIBRARY="${FT2_DIR}/lib/libfreetype.so" \
|
||||||
|
-DFREETYPE_INCLUDE_DIRS="${FT2_DIR}/include/freetype2" \
|
||||||
|
-DFontconfig_LIBRARY="${FC_DIR}/lib/libfontconfig.so" \
|
||||||
|
-DFontconfig_INCLUDE_DIR="${FC_DIR}/include" \
|
||||||
-DBUILD_TESTING=OFF \
|
-DBUILD_TESTING=OFF \
|
||||||
-DBUILD_QCH=OFF \
|
-DBUILD_QCH=OFF \
|
||||||
|
-DKWIN_BUILD_AUTO_ROTATION=OFF \
|
||||||
-DKWIN_BUILD_X11=OFF \
|
-DKWIN_BUILD_X11=OFF \
|
||||||
|
-DKWIN_BUILD_X11_BACKEND=OFF \
|
||||||
-DKWIN_BUILD_KCMS=OFF \
|
-DKWIN_BUILD_KCMS=OFF \
|
||||||
-DKWIN_BUILD_SCREENLOCKER=OFF \
|
-DKWIN_BUILD_SCREENLOCKER=OFF \
|
||||||
-DKWIN_BUILD_TABBOX=OFF \
|
-DKWIN_BUILD_TABBOX=OFF \
|
||||||
-DKWIN_BUILD_GLOBALSHORTCUTS=OFF \
|
-DKWIN_BUILD_GLOBALSHORTCUTS=OFF \
|
||||||
-DKWIN_BUILD_RUNNERS=OFF \
|
-DKWIN_BUILD_RUNNERS=OFF \
|
||||||
-DKWIN_BUILD_NOTIFICATIONS=OFF \
|
-DKWIN_BUILD_NOTIFICATIONS=OFF \
|
||||||
-Wno-dev 2>&1
|
-DKWIN_BUILD_ACTIVITIES=OFF \
|
||||||
cmake --build . -j${COOKBOOK_MAKE_JOBS} 2>&1
|
-DKWIN_BUILD_EIS=OFF \
|
||||||
cmake --install . --prefix "${STAGE}" 2>&1
|
-DQt6GuiPrivate_DIR="${COOKBOOK_SYSROOT}/lib/cmake/Qt6GuiPrivate" \
|
||||||
|
-DQt6CorePrivate_DIR="${COOKBOOK_SYSROOT}/lib/cmake/Qt6CorePrivate" \
|
||||||
|
-DQT_FIND_PRIVATE_MODULES=ON \
|
||||||
|
-DQTWAYLANDSCANNER_KDE_EXECUTABLE="${HOST_SCANNER}" \
|
||||||
|
-DPLASMA_WAYLAND_PROTOCOLS_DIR="${COOKBOOK_SYSROOT}/usr/share/plasma-wayland-protocols" \
|
||||||
|
-DCMAKE_BUILD_WITH_INSTALL_RPATH=TRUE \
|
||||||
|
-DCMAKE_INSTALL_RPATH="/usr/lib" \
|
||||||
|
-Wno-dev
|
||||||
|
|
||||||
# Downstream cmake configs for KF6WindowSystem/KF6Config (needed by plasma-framework, plasma-workspace, plasma-desktop — not KWin itself)
|
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||||
mkdir -p "${STAGE}/lib/cmake/KF6WindowSystem" "${STAGE}/lib/cmake/KF6Config"
|
cmake --install . --prefix "${STAGE}"
|
||||||
cat > "${STAGE}/lib/cmake/KF6WindowSystem/KF6WindowSystemConfig.cmake" << 'EOFCMAKE'
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
for bin in "${STAGE}/bin/"* "${STAGE}/lib/"lib*.so.*; do
|
||||||
set(KF6WindowSystem_LIBRARIES Qt6::Gui)
|
[ -f "${bin}" ] || continue
|
||||||
EOFCMAKE
|
patchelf --set-rpath "/usr/lib" "${bin}" 2>/dev/null || true
|
||||||
cat > "${STAGE}/lib/cmake/KF6Config/KF6ConfigConfig.cmake" << 'EOFCMAKE'
|
done
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core)
|
|
||||||
set(KF6Config_LIBRARIES Qt6::Core)
|
|
||||||
EOFCMAKE
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[source]
|
||||||
|
tar = "https://sourceforge.net/projects/freetype/files/freetype2/2.13.3/freetype-2.13.3.tar.xz/download"
|
||||||
|
blake3 = "07a01894ccdb584943ce817b57341a8595ce9a92bfaa77c602ec4757dfabd5e2"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
dependencies = [
|
||||||
|
"libpng",
|
||||||
|
"zlib"
|
||||||
|
]
|
||||||
|
script = """
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
cookbook_meson -Dpng=disabled
|
||||||
|
"""
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[source]
|
||||||
|
tar = "https://github.com/mm2/Little-CMS/archive/refs/tags/lcms2.19.tar.gz"
|
||||||
|
blake3 = "730f873079fc24b195f87557c872814206805242e977960ee9e8aff8cd6ab28b"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
script = """
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
|
||||||
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_PREFIX_PATH="${COOKBOOK_SYSROOT}" \
|
||||||
|
-DBUILD_SHARED_LIBS=ON \
|
||||||
|
-DHCMS2_BUILD_TESTS=OFF \
|
||||||
|
-DHCMS2_BUILD_TOOLS=OFF \
|
||||||
|
-Wno-dev
|
||||||
|
|
||||||
|
cmake --build . -j${COOKBOOK_MAKE_JOBS}
|
||||||
|
cmake --install . --prefix "${COOKBOOK_STAGE}/usr"
|
||||||
|
|
||||||
|
for lib in "${COOKBOOK_STAGE}/usr/lib/"lib*.so.*; do
|
||||||
|
[ -f "${lib}" ] || continue
|
||||||
|
patchelf --remove-rpath "${lib}" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
"""
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
[source]
|
||||||
|
path = "source"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
script = '''
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
python3 - <<'PY' "${COOKBOOK_SOURCE}"
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
source_root = Path(sys.argv[1])
|
||||||
|
meson_build = source_root / "meson.build"
|
||||||
|
|
||||||
|
if not meson_build.exists():
|
||||||
|
meson_build.write_text(
|
||||||
|
"""project('libdisplay-info', 'c',
|
||||||
|
version: '0.2.0',
|
||||||
|
meson_version: '>= 0.54.0',
|
||||||
|
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||||
|
|
||||||
|
inc = include_directories('include')
|
||||||
|
|
||||||
|
libdisplay_info = shared_library('display-info',
|
||||||
|
'di.c',
|
||||||
|
include_directories: inc,
|
||||||
|
version: meson.project_version(),
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
install_headers(
|
||||||
|
'include/libdisplay-info/cta.h',
|
||||||
|
'include/libdisplay-info/displayid.h',
|
||||||
|
'include/libdisplay-info/edid.h',
|
||||||
|
'include/libdisplay-info/info.h',
|
||||||
|
subdir: 'libdisplay-info',
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg = import('pkgconfig')
|
||||||
|
pkg.generate(
|
||||||
|
libdisplay_info,
|
||||||
|
name: 'libdisplay-info',
|
||||||
|
description: 'Display identification data parsing library',
|
||||||
|
filebase: 'libdisplay-info',
|
||||||
|
version: meson.project_version(),
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
PY
|
||||||
|
|
||||||
|
cookbook_meson
|
||||||
|
'''
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
project('libdisplay-info', 'c',
|
||||||
|
version: '0.2.0',
|
||||||
|
meson_version: '>= 0.54.0',
|
||||||
|
default_options: ['warning_level=1', 'buildtype=debugoptimized'])
|
||||||
|
|
||||||
|
inc = include_directories('include')
|
||||||
|
|
||||||
|
libdisplay_info = shared_library('display-info',
|
||||||
|
'di.c',
|
||||||
|
include_directories: inc,
|
||||||
|
version: meson.project_version(),
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
install_headers(
|
||||||
|
'include/libdisplay-info/cta.h',
|
||||||
|
'include/libdisplay-info/displayid.h',
|
||||||
|
'include/libdisplay-info/edid.h',
|
||||||
|
'include/libdisplay-info/info.h',
|
||||||
|
subdir: 'libdisplay-info',
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg = import('pkgconfig')
|
||||||
|
pkg.generate(
|
||||||
|
libdisplay_info,
|
||||||
|
name: 'libdisplay-info',
|
||||||
|
description: 'Display identification data parsing library',
|
||||||
|
filebase: 'libdisplay-info',
|
||||||
|
version: meson.project_version(),
|
||||||
|
)
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,7 @@
|
|||||||
#include "libdrm_macros.h"
|
#include "libdrm_macros.h"
|
||||||
#include "xf86drmMode.h"
|
#include "xf86drmMode.h"
|
||||||
#include "xf86drm.h"
|
#include "xf86drm.h"
|
||||||
|
#include "xf86drm_redox.h"
|
||||||
#include <drm.h>
|
#include <drm.h>
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -68,6 +69,212 @@ static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
|
|||||||
return ret < 0 ? -errno : ret;
|
return ret < 0 ? -errno : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void* drmAllocCpy(char *array, int count, int entry_size);
|
||||||
|
|
||||||
|
#if defined(__redox__)
|
||||||
|
static void redox_drm_mode_from_wire(drmModeModeInfoPtr mode,
|
||||||
|
const struct redox_drm_mode_wire *wire,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
memclear(*mode);
|
||||||
|
mode->clock = wire->clock;
|
||||||
|
mode->hdisplay = wire->hdisplay;
|
||||||
|
mode->hsync_start = wire->hsync_start;
|
||||||
|
mode->hsync_end = wire->hsync_end;
|
||||||
|
mode->htotal = wire->htotal;
|
||||||
|
mode->hskew = wire->hskew;
|
||||||
|
mode->vdisplay = wire->vdisplay;
|
||||||
|
mode->vsync_start = wire->vsync_start;
|
||||||
|
mode->vsync_end = wire->vsync_end;
|
||||||
|
mode->vtotal = wire->vtotal;
|
||||||
|
mode->vscan = wire->vscan;
|
||||||
|
mode->vrefresh = wire->vrefresh;
|
||||||
|
mode->flags = wire->flags;
|
||||||
|
mode->type = wire->type;
|
||||||
|
if (name) {
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
if (name_len >= DRM_DISPLAY_MODE_LEN)
|
||||||
|
name_len = DRM_DISPLAY_MODE_LEN - 1;
|
||||||
|
memcpy(mode->name, name, name_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redox_drm_mode_to_wire(struct redox_drm_mode_wire *wire,
|
||||||
|
const drmModeModeInfo *mode)
|
||||||
|
{
|
||||||
|
memclear(*wire);
|
||||||
|
wire->clock = mode->clock;
|
||||||
|
wire->hdisplay = mode->hdisplay;
|
||||||
|
wire->hsync_start = mode->hsync_start;
|
||||||
|
wire->hsync_end = mode->hsync_end;
|
||||||
|
wire->htotal = mode->htotal;
|
||||||
|
wire->hskew = mode->hskew;
|
||||||
|
wire->vdisplay = mode->vdisplay;
|
||||||
|
wire->vsync_start = mode->vsync_start;
|
||||||
|
wire->vsync_end = mode->vsync_end;
|
||||||
|
wire->vtotal = mode->vtotal;
|
||||||
|
wire->vscan = mode->vscan;
|
||||||
|
wire->vrefresh = mode->vrefresh;
|
||||||
|
wire->flags = mode->flags;
|
||||||
|
wire->type = mode->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redox_drm_fetch_resources(int fd,
|
||||||
|
struct redox_drm_resources_wire *header,
|
||||||
|
uint32_t **connector_ids)
|
||||||
|
{
|
||||||
|
void *response = NULL;
|
||||||
|
size_t response_size = 0;
|
||||||
|
size_t expected_size;
|
||||||
|
|
||||||
|
if (redox_drm_exchange_alloc(fd, REDOX_DRM_IOCTL_MODE_GETRESOURCES,
|
||||||
|
NULL, 0,
|
||||||
|
&response, &response_size) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response_size < sizeof(*header)) {
|
||||||
|
drmFree(response);
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(header, response, sizeof(*header));
|
||||||
|
expected_size = sizeof(*header) +
|
||||||
|
((size_t)header->connector_count * sizeof(uint32_t));
|
||||||
|
if (response_size < expected_size) {
|
||||||
|
drmFree(response);
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*connector_ids = NULL;
|
||||||
|
if (header->connector_count != 0) {
|
||||||
|
*connector_ids = drmAllocCpy((char *)response + sizeof(*header),
|
||||||
|
header->connector_count, sizeof(uint32_t));
|
||||||
|
if (!*connector_ids) {
|
||||||
|
drmFree(response);
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFree(response);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redox_drm_parse_connector_response(const void *blob, size_t blob_size,
|
||||||
|
struct redox_drm_connector_wire *header,
|
||||||
|
drmModeModeInfoPtr *modes_out)
|
||||||
|
{
|
||||||
|
const uint8_t *bytes = blob;
|
||||||
|
drmModeModeInfoPtr modes = NULL;
|
||||||
|
size_t offset = sizeof(*header);
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (blob_size < sizeof(*header)) {
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(header, bytes, sizeof(*header));
|
||||||
|
if (header->mode_count != 0) {
|
||||||
|
modes = drmMalloc(header->mode_count * sizeof(*modes));
|
||||||
|
if (!modes) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < header->mode_count; ++i) {
|
||||||
|
struct redox_drm_mode_wire mode_wire;
|
||||||
|
const char *name;
|
||||||
|
size_t name_len;
|
||||||
|
|
||||||
|
if (blob_size - offset < sizeof(mode_wire)) {
|
||||||
|
drmFree(modes);
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&mode_wire, bytes + offset, sizeof(mode_wire));
|
||||||
|
offset += sizeof(mode_wire);
|
||||||
|
name = (const char *)bytes + offset;
|
||||||
|
name_len = strnlen(name, blob_size - offset);
|
||||||
|
if (offset + name_len >= blob_size) {
|
||||||
|
drmFree(modes);
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
redox_drm_mode_from_wire(&modes[i], &mode_wire, name);
|
||||||
|
offset += name_len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*modes_out = modes;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redox_drm_fetch_connector(int fd, uint32_t connector_id,
|
||||||
|
struct redox_drm_connector_wire *header,
|
||||||
|
drmModeModeInfoPtr *modes_out)
|
||||||
|
{
|
||||||
|
void *response = NULL;
|
||||||
|
size_t response_size = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (redox_drm_exchange_alloc(fd, REDOX_DRM_IOCTL_MODE_GETCONNECTOR,
|
||||||
|
&connector_id, sizeof(connector_id),
|
||||||
|
&response, &response_size) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = redox_drm_parse_connector_response(response, response_size,
|
||||||
|
header, modes_out);
|
||||||
|
drmFree(response);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t redox_drm_connector_type_id(int fd, uint32_t connector_id,
|
||||||
|
uint32_t connector_type)
|
||||||
|
{
|
||||||
|
struct redox_drm_resources_wire resources;
|
||||||
|
uint32_t *connector_ids = NULL;
|
||||||
|
uint32_t ordinal = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < resources.connector_count; ++i) {
|
||||||
|
struct redox_drm_connector_wire header;
|
||||||
|
drmModeModeInfoPtr modes = NULL;
|
||||||
|
|
||||||
|
if (redox_drm_fetch_connector(fd, connector_ids[i], &header, &modes) != 0)
|
||||||
|
break;
|
||||||
|
drmFree(modes);
|
||||||
|
|
||||||
|
if (header.connector_type == connector_type)
|
||||||
|
++ordinal;
|
||||||
|
if (header.connector_id == connector_id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFree(connector_ids);
|
||||||
|
return ordinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int redox_drm_page_flip_unsupported(uint32_t flags)
|
||||||
|
{
|
||||||
|
if (flags & (DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_TARGET)) {
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Util functions
|
* Util functions
|
||||||
*/
|
*/
|
||||||
@@ -153,16 +360,104 @@ drm_public void drmModeFreeEncoder(drmModeEncoderPtr ptr)
|
|||||||
|
|
||||||
drm_public int drmIsKMS(int fd)
|
drm_public int drmIsKMS(int fd)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_resources_wire resources;
|
||||||
|
uint32_t *connector_ids = NULL;
|
||||||
|
int is_kms;
|
||||||
|
|
||||||
|
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
is_kms = resources.crtc_count > 0 &&
|
||||||
|
resources.connector_count > 0 &&
|
||||||
|
resources.encoder_count > 0;
|
||||||
|
drmFree(connector_ids);
|
||||||
|
return is_kms;
|
||||||
|
#else
|
||||||
struct drm_mode_card_res res = {0};
|
struct drm_mode_card_res res = {0};
|
||||||
|
|
||||||
if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0)
|
if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return res.count_crtcs > 0 && res.count_connectors > 0 && res.count_encoders > 0;
|
return res.count_crtcs > 0 && res.count_connectors > 0 && res.count_encoders > 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public drmModeResPtr drmModeGetResources(int fd)
|
drm_public drmModeResPtr drmModeGetResources(int fd)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_resources_wire resources;
|
||||||
|
uint32_t *connector_ids = NULL;
|
||||||
|
drmModeResPtr r = NULL;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (redox_drm_fetch_resources(fd, &resources, &connector_ids) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
r = drmMalloc(sizeof(*r));
|
||||||
|
if (!r) {
|
||||||
|
drmFree(connector_ids);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memclear(*r);
|
||||||
|
|
||||||
|
r->count_connectors = resources.connector_count;
|
||||||
|
r->count_crtcs = resources.crtc_count;
|
||||||
|
r->count_encoders = resources.encoder_count;
|
||||||
|
r->connectors = connector_ids;
|
||||||
|
|
||||||
|
if (resources.crtc_count != 0) {
|
||||||
|
r->crtcs = drmMalloc(resources.crtc_count * sizeof(*r->crtcs));
|
||||||
|
if (!r->crtcs)
|
||||||
|
goto err_allocs;
|
||||||
|
for (i = 0; i < resources.crtc_count; ++i)
|
||||||
|
r->crtcs[i] = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.encoder_count != 0) {
|
||||||
|
r->encoders = drmMalloc(resources.encoder_count * sizeof(*r->encoders));
|
||||||
|
if (!r->encoders)
|
||||||
|
goto err_allocs;
|
||||||
|
memset(r->encoders, 0, resources.encoder_count * sizeof(*r->encoders));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < resources.connector_count; ++i) {
|
||||||
|
struct redox_drm_connector_wire connector;
|
||||||
|
drmModeModeInfoPtr modes = NULL;
|
||||||
|
uint32_t j;
|
||||||
|
|
||||||
|
if (redox_drm_fetch_connector(fd, connector_ids[i], &connector, &modes) != 0)
|
||||||
|
goto err_allocs;
|
||||||
|
|
||||||
|
if (i < resources.encoder_count)
|
||||||
|
r->encoders[i] = connector.encoder_id;
|
||||||
|
|
||||||
|
for (j = 0; j < connector.mode_count; ++j) {
|
||||||
|
uint32_t width = modes[j].hdisplay;
|
||||||
|
uint32_t height = modes[j].vdisplay;
|
||||||
|
if (r->min_width == 0 || width < r->min_width)
|
||||||
|
r->min_width = width;
|
||||||
|
if (width > r->max_width)
|
||||||
|
r->max_width = width;
|
||||||
|
if (r->min_height == 0 || height < r->min_height)
|
||||||
|
r->min_height = height;
|
||||||
|
if (height > r->max_height)
|
||||||
|
r->max_height = height;
|
||||||
|
}
|
||||||
|
drmFree(modes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.encoder_count > resources.connector_count) {
|
||||||
|
for (i = resources.connector_count; i < resources.encoder_count; ++i)
|
||||||
|
r->encoders[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
|
||||||
|
err_allocs:
|
||||||
|
drmModeFreeResources(r);
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
struct drm_mode_card_res res, counts;
|
struct drm_mode_card_res res, counts;
|
||||||
drmModeResPtr r = 0;
|
drmModeResPtr r = 0;
|
||||||
|
|
||||||
@@ -253,6 +548,7 @@ err_allocs:
|
|||||||
drmFree(U642VOID(res.encoder_id_ptr));
|
drmFree(U642VOID(res.encoder_id_ptr));
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -260,6 +556,26 @@ drm_public int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t dep
|
|||||||
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
|
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
|
||||||
uint32_t *buf_id)
|
uint32_t *buf_id)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_add_fb_wire wire;
|
||||||
|
|
||||||
|
memclear(wire);
|
||||||
|
wire.width = width;
|
||||||
|
wire.height = height;
|
||||||
|
wire.pitch = pitch;
|
||||||
|
wire.bpp = bpp;
|
||||||
|
wire.depth = depth;
|
||||||
|
wire.handle = bo_handle;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_ADDFB,
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf_id = wire.fb_id;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
struct drm_mode_fb_cmd f;
|
struct drm_mode_fb_cmd f;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -276,6 +592,7 @@ drm_public int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t dep
|
|||||||
|
|
||||||
*buf_id = f.fb_id;
|
*buf_id = f.fb_id;
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int drmModeAddFB2WithModifiers(int fd, uint32_t width,
|
drm_public int drmModeAddFB2WithModifiers(int fd, uint32_t width,
|
||||||
@@ -317,7 +634,22 @@ drm_public int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
|
|||||||
|
|
||||||
drm_public int drmModeRmFB(int fd, uint32_t bufferId)
|
drm_public int drmModeRmFB(int fd, uint32_t bufferId)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_rm_fb_wire wire;
|
||||||
|
|
||||||
|
memclear(wire);
|
||||||
|
wire.fb_id = bufferId;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_RMFB,
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
NULL, 0,
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_RMFB, &bufferId);
|
return DRM_IOCTL(fd, DRM_IOCTL_MODE_RMFB, &bufferId);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int drmModeCloseFB(int fd, uint32_t buffer_id)
|
drm_public int drmModeCloseFB(int fd, uint32_t buffer_id)
|
||||||
@@ -374,6 +706,40 @@ drm_public int drmModeDirtyFB(int fd, uint32_t bufferId,
|
|||||||
|
|
||||||
drm_public drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
drm_public drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_get_crtc_wire wire;
|
||||||
|
drmModeCrtcPtr r;
|
||||||
|
|
||||||
|
memclear(wire);
|
||||||
|
wire.crtc_id = crtcId;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_GETCRTC,
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
NULL) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = drmMalloc(sizeof(*r));
|
||||||
|
if (!r)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memclear(*r);
|
||||||
|
r->crtc_id = wire.crtc_id;
|
||||||
|
r->x = wire.x;
|
||||||
|
r->y = wire.y;
|
||||||
|
r->mode_valid = wire.mode_valid;
|
||||||
|
if (r->mode_valid) {
|
||||||
|
char mode_name[DRM_DISPLAY_MODE_LEN];
|
||||||
|
snprintf(mode_name, sizeof(mode_name), "%ux%u@%u",
|
||||||
|
wire.mode.hdisplay, wire.mode.vdisplay, wire.mode.vrefresh);
|
||||||
|
redox_drm_mode_from_wire(&r->mode, &wire.mode, mode_name);
|
||||||
|
r->width = wire.mode.hdisplay;
|
||||||
|
r->height = wire.mode.vdisplay;
|
||||||
|
}
|
||||||
|
r->buffer_id = wire.fb_id;
|
||||||
|
r->gamma_size = 0;
|
||||||
|
return r;
|
||||||
|
#else
|
||||||
struct drm_mode_crtc crtc;
|
struct drm_mode_crtc crtc;
|
||||||
drmModeCrtcPtr r;
|
drmModeCrtcPtr r;
|
||||||
|
|
||||||
@@ -402,12 +768,47 @@ drm_public drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
|
|||||||
r->buffer_id = crtc.fb_id;
|
r->buffer_id = crtc.fb_id;
|
||||||
r->gamma_size = crtc.gamma_size;
|
r->gamma_size = crtc.gamma_size;
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
drm_public int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
||||||
uint32_t x, uint32_t y, uint32_t *connectors, int count,
|
uint32_t x, uint32_t y, uint32_t *connectors, int count,
|
||||||
drmModeModeInfoPtr mode)
|
drmModeModeInfoPtr mode)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_set_crtc_wire wire;
|
||||||
|
|
||||||
|
if ((x != 0 || y != 0) && bufferId != 0) {
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
if (count < 0 || count > 8) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
if (bufferId != 0 && !mode) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
memclear(wire);
|
||||||
|
wire.crtc_id = crtcId;
|
||||||
|
wire.fb_handle = bufferId;
|
||||||
|
wire.connector_count = count;
|
||||||
|
if (count > 0)
|
||||||
|
memcpy(wire.connectors, connectors, count * sizeof(*connectors));
|
||||||
|
if (mode)
|
||||||
|
redox_drm_mode_to_wire(&wire.mode, mode);
|
||||||
|
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_SETCRTC,
|
||||||
|
&wire, sizeof(wire),
|
||||||
|
NULL, 0,
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
struct drm_mode_crtc crtc;
|
struct drm_mode_crtc crtc;
|
||||||
|
|
||||||
memclear(crtc);
|
memclear(crtc);
|
||||||
@@ -423,6 +824,7 @@ drm_public int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
|
return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -480,6 +882,30 @@ drm_public int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
|
|||||||
*/
|
*/
|
||||||
drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct drm_mode_get_encoder enc;
|
||||||
|
drmModeEncoderPtr r = NULL;
|
||||||
|
|
||||||
|
memclear(enc);
|
||||||
|
enc.encoder_id = encoder_id;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_GETENCODER,
|
||||||
|
&enc, sizeof(enc),
|
||||||
|
&enc, sizeof(enc),
|
||||||
|
NULL) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = drmMalloc(sizeof(*r));
|
||||||
|
if (!r)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
r->encoder_id = enc.encoder_id;
|
||||||
|
r->crtc_id = enc.crtc_id;
|
||||||
|
r->encoder_type = enc.encoder_type;
|
||||||
|
r->possible_crtcs = enc.possible_crtcs;
|
||||||
|
r->possible_clones = enc.possible_clones;
|
||||||
|
return r;
|
||||||
|
#else
|
||||||
struct drm_mode_get_encoder enc;
|
struct drm_mode_get_encoder enc;
|
||||||
drmModeEncoderPtr r = NULL;
|
drmModeEncoderPtr r = NULL;
|
||||||
|
|
||||||
@@ -499,6 +925,7 @@ drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
|||||||
r->possible_clones = enc.possible_clones;
|
r->possible_clones = enc.possible_clones;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -507,6 +934,50 @@ drm_public drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
|
|||||||
static drmModeConnectorPtr
|
static drmModeConnectorPtr
|
||||||
_drmModeGetConnector(int fd, uint32_t connector_id, int probe)
|
_drmModeGetConnector(int fd, uint32_t connector_id, int probe)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_connector_wire conn;
|
||||||
|
drmModeConnectorPtr r = NULL;
|
||||||
|
drmModeModeInfoPtr modes = NULL;
|
||||||
|
|
||||||
|
(void)probe;
|
||||||
|
if (redox_drm_fetch_connector(fd, connector_id, &conn, &modes) != 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
r = drmMalloc(sizeof(*r));
|
||||||
|
if (!r)
|
||||||
|
goto err_allocs;
|
||||||
|
memclear(*r);
|
||||||
|
|
||||||
|
r->connector_id = conn.connector_id;
|
||||||
|
r->encoder_id = conn.encoder_id;
|
||||||
|
r->connection = conn.connection;
|
||||||
|
r->mmWidth = conn.mm_width;
|
||||||
|
r->mmHeight = conn.mm_height;
|
||||||
|
r->subpixel = DRM_MODE_SUBPIXEL_UNKNOWN;
|
||||||
|
r->count_modes = conn.mode_count;
|
||||||
|
r->modes = modes;
|
||||||
|
r->count_props = 0;
|
||||||
|
r->props = NULL;
|
||||||
|
r->prop_values = NULL;
|
||||||
|
r->count_encoders = conn.encoder_id ? 1 : 0;
|
||||||
|
if (r->count_encoders != 0) {
|
||||||
|
r->encoders = drmMalloc(sizeof(*r->encoders));
|
||||||
|
if (!r->encoders)
|
||||||
|
goto err_allocs;
|
||||||
|
r->encoders[0] = conn.encoder_id;
|
||||||
|
}
|
||||||
|
r->connector_type = conn.connector_type;
|
||||||
|
r->connector_type_id = redox_drm_connector_type_id(fd,
|
||||||
|
conn.connector_id,
|
||||||
|
conn.connector_type);
|
||||||
|
return r;
|
||||||
|
|
||||||
|
err_allocs:
|
||||||
|
drmFree(modes);
|
||||||
|
drmFree(r ? r->encoders : NULL);
|
||||||
|
drmFree(r);
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
struct drm_mode_get_connector conn, counts;
|
struct drm_mode_get_connector conn, counts;
|
||||||
drmModeConnectorPtr r = NULL;
|
drmModeConnectorPtr r = NULL;
|
||||||
struct drm_mode_modeinfo stack_mode;
|
struct drm_mode_modeinfo stack_mode;
|
||||||
@@ -608,6 +1079,7 @@ err_allocs:
|
|||||||
drmFree(U642VOID(conn.encoders_ptr));
|
drmFree(U642VOID(conn.encoders_ptr));
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
|
drm_public drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
|
||||||
@@ -1100,6 +1572,27 @@ drm_public int drmHandleEvent(int fd, drmEventContextPtr evctx)
|
|||||||
drm_public int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
|
drm_public int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||||
uint32_t flags, void *user_data)
|
uint32_t flags, void *user_data)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_page_flip_wire flip;
|
||||||
|
uint64_t sequence;
|
||||||
|
|
||||||
|
(void)user_data;
|
||||||
|
if (redox_drm_page_flip_unsupported(flags) != 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
memclear(flip);
|
||||||
|
flip.fb_handle = fb_id;
|
||||||
|
flip.crtc_id = crtc_id;
|
||||||
|
flip.flags = flags;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_PAGE_FLIP,
|
||||||
|
&flip, sizeof(flip),
|
||||||
|
&sequence, sizeof(sequence),
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
struct drm_mode_crtc_page_flip flip;
|
struct drm_mode_crtc_page_flip flip;
|
||||||
|
|
||||||
memclear(flip);
|
memclear(flip);
|
||||||
@@ -1109,12 +1602,23 @@ drm_public int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
|
|||||||
flip.flags = flags;
|
flip.flags = flags;
|
||||||
|
|
||||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
|
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
|
drm_public int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
|
||||||
uint32_t flags, void *user_data,
|
uint32_t flags, void *user_data,
|
||||||
uint32_t target_vblank)
|
uint32_t target_vblank)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
(void)fd;
|
||||||
|
(void)crtc_id;
|
||||||
|
(void)fb_id;
|
||||||
|
(void)flags;
|
||||||
|
(void)user_data;
|
||||||
|
(void)target_vblank;
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -errno;
|
||||||
|
#else
|
||||||
struct drm_mode_crtc_page_flip_target flip_target;
|
struct drm_mode_crtc_page_flip_target flip_target;
|
||||||
|
|
||||||
memclear(flip_target);
|
memclear(flip_target);
|
||||||
@@ -1125,6 +1629,7 @@ drm_public int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
|
|||||||
flip_target.sequence = target_vblank;
|
flip_target.sequence = target_vblank;
|
||||||
|
|
||||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip_target);
|
return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip_target);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id,
|
drm_public int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id,
|
||||||
@@ -1839,6 +2344,26 @@ drmModeCreateDumbBuffer(int fd, uint32_t width, uint32_t height, uint32_t bpp,
|
|||||||
uint32_t flags, uint32_t *handle, uint32_t *pitch,
|
uint32_t flags, uint32_t *handle, uint32_t *pitch,
|
||||||
uint64_t *size)
|
uint64_t *size)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_create_dumb_wire create;
|
||||||
|
|
||||||
|
memclear(create);
|
||||||
|
create.width = width;
|
||||||
|
create.height = height;
|
||||||
|
create.bpp = bpp;
|
||||||
|
create.flags = flags;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_CREATE_DUMB,
|
||||||
|
&create, sizeof(create),
|
||||||
|
&create, sizeof(create),
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
*handle = create.handle;
|
||||||
|
*pitch = create.pitch;
|
||||||
|
*size = create.size;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
int ret;
|
int ret;
|
||||||
struct drm_mode_create_dumb create = {
|
struct drm_mode_create_dumb create = {
|
||||||
.width = width,
|
.width = width,
|
||||||
@@ -1855,21 +2380,52 @@ drmModeCreateDumbBuffer(int fd, uint32_t width, uint32_t height, uint32_t bpp,
|
|||||||
*pitch = create.pitch;
|
*pitch = create.pitch;
|
||||||
*size = create.size;
|
*size = create.size;
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int
|
drm_public int
|
||||||
drmModeDestroyDumbBuffer(int fd, uint32_t handle)
|
drmModeDestroyDumbBuffer(int fd, uint32_t handle)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_destroy_dumb_wire destroy;
|
||||||
|
|
||||||
|
memclear(destroy);
|
||||||
|
destroy.handle = handle;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_DESTROY_DUMB,
|
||||||
|
&destroy, sizeof(destroy),
|
||||||
|
NULL, 0,
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
struct drm_mode_destroy_dumb destroy = {
|
struct drm_mode_destroy_dumb destroy = {
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
return DRM_IOCTL(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
|
return DRM_IOCTL(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_public int
|
drm_public int
|
||||||
drmModeMapDumbBuffer(int fd, uint32_t handle, uint64_t *offset)
|
drmModeMapDumbBuffer(int fd, uint32_t handle, uint64_t *offset)
|
||||||
{
|
{
|
||||||
|
#if defined(__redox__)
|
||||||
|
struct redox_drm_map_dumb_wire map;
|
||||||
|
|
||||||
|
memclear(map);
|
||||||
|
map.handle = handle;
|
||||||
|
if (redox_drm_exchange(fd, REDOX_DRM_IOCTL_MODE_MAP_DUMB,
|
||||||
|
&map, sizeof(map),
|
||||||
|
&map, sizeof(map),
|
||||||
|
NULL) != 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
*offset = map.offset;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
int ret;
|
int ret;
|
||||||
struct drm_mode_map_dumb map = {
|
struct drm_mode_map_dumb map = {
|
||||||
.handle = handle,
|
.handle = handle,
|
||||||
@@ -1881,4 +2437,5 @@ drmModeMapDumbBuffer(int fd, uint32_t handle, uint64_t *offset)
|
|||||||
|
|
||||||
*offset = map.offset;
|
*offset = map.offset;
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
#ifndef _XF86DRM_REDOX_H_
|
||||||
|
#define _XF86DRM_REDOX_H_
|
||||||
|
|
||||||
|
#if defined(__redox__)
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define REDOX_DRM_IOCTL_BASE 0x00A0UL
|
||||||
|
|
||||||
|
#define REDOX_LINUX_IOCTL_NR(request) ((unsigned long)((request) & 0xffUL))
|
||||||
|
#define REDOX_LINUX_IOCTL_SIZE(request) (((unsigned long)(request) >> 16) & 0x3fffUL)
|
||||||
|
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETRESOURCES (REDOX_DRM_IOCTL_BASE + 0)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_SETCRTC (REDOX_DRM_IOCTL_BASE + 2)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETCRTC (REDOX_DRM_IOCTL_BASE + 3)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETENCODER (REDOX_DRM_IOCTL_BASE + 6)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETCONNECTOR (REDOX_DRM_IOCTL_BASE + 7)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_PAGE_FLIP (REDOX_DRM_IOCTL_BASE + 16)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_CREATE_DUMB (REDOX_DRM_IOCTL_BASE + 18)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_MAP_DUMB (REDOX_DRM_IOCTL_BASE + 19)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_DESTROY_DUMB (REDOX_DRM_IOCTL_BASE + 20)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_ADDFB (REDOX_DRM_IOCTL_BASE + 21)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_RMFB (REDOX_DRM_IOCTL_BASE + 22)
|
||||||
|
#define REDOX_DRM_IOCTL_GET_CAP (REDOX_DRM_IOCTL_BASE + 23)
|
||||||
|
#define REDOX_DRM_IOCTL_SET_CLIENT_CAP (REDOX_DRM_IOCTL_BASE + 24)
|
||||||
|
#define REDOX_DRM_IOCTL_VERSION (REDOX_DRM_IOCTL_BASE + 25)
|
||||||
|
#define REDOX_DRM_IOCTL_GEM_CLOSE (REDOX_DRM_IOCTL_BASE + 27)
|
||||||
|
#define REDOX_DRM_IOCTL_GEM_FLINK (REDOX_DRM_IOCTL_BASE + 10)
|
||||||
|
#define REDOX_DRM_IOCTL_GEM_OPEN (REDOX_DRM_IOCTL_BASE + 11)
|
||||||
|
#define REDOX_DRM_IOCTL_PRIME_HANDLE_TO_FD (REDOX_DRM_IOCTL_BASE + 29)
|
||||||
|
#define REDOX_DRM_IOCTL_PRIME_FD_TO_HANDLE (REDOX_DRM_IOCTL_BASE + 30)
|
||||||
|
#define REDOX_DRM_IOCTL_GET_MAGIC (REDOX_DRM_IOCTL_BASE + 33)
|
||||||
|
#define REDOX_DRM_IOCTL_AUTH_MAGIC (REDOX_DRM_IOCTL_BASE + 34)
|
||||||
|
#define REDOX_DRM_IOCTL_SET_MASTER (REDOX_DRM_IOCTL_BASE + 35)
|
||||||
|
#define REDOX_DRM_IOCTL_DROP_MASTER (REDOX_DRM_IOCTL_BASE + 36)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_OBJ_GETPROPERTIES (REDOX_DRM_IOCTL_BASE + 0x50)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETPROPERTY (REDOX_DRM_IOCTL_BASE + 0x51)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETPROPBLOB (REDOX_DRM_IOCTL_BASE + 0x52)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_CREATE_LEASE (REDOX_DRM_IOCTL_BASE + 0x53)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_LIST_LESSEES (REDOX_DRM_IOCTL_BASE + 0x54)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_OBJ_SETPROPERTY (REDOX_DRM_IOCTL_BASE + 0x55)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETPLANERESOURCES (REDOX_DRM_IOCTL_BASE + 0x56)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_GETPLANE (REDOX_DRM_IOCTL_BASE + 0x57)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_SETPLANE (REDOX_DRM_IOCTL_BASE + 0x58)
|
||||||
|
#define REDOX_DRM_IOCTL_MODE_ADDFB2 (REDOX_DRM_IOCTL_BASE + 0x59)
|
||||||
|
#define REDOX_DRM_IOCTL_GET_PCI_INFO (REDOX_DRM_IOCTL_BASE + 0x60)
|
||||||
|
|
||||||
|
struct redox_drm_pci_info_wire {
|
||||||
|
uint16_t vendor_id;
|
||||||
|
uint16_t device_id;
|
||||||
|
uint16_t subvendor_id;
|
||||||
|
uint16_t subdevice_id;
|
||||||
|
uint8_t revision_id;
|
||||||
|
uint8_t pad[3];
|
||||||
|
uint16_t domain;
|
||||||
|
uint8_t bus;
|
||||||
|
uint8_t dev;
|
||||||
|
uint8_t func;
|
||||||
|
uint8_t pad2[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_resources_wire {
|
||||||
|
uint32_t connector_count;
|
||||||
|
uint32_t crtc_count;
|
||||||
|
uint32_t encoder_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_connector_wire {
|
||||||
|
uint32_t connector_id;
|
||||||
|
uint32_t connection;
|
||||||
|
uint32_t connector_type;
|
||||||
|
uint32_t mm_width;
|
||||||
|
uint32_t mm_height;
|
||||||
|
uint32_t encoder_id;
|
||||||
|
uint32_t mode_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_mode_wire {
|
||||||
|
uint32_t clock;
|
||||||
|
uint16_t hdisplay;
|
||||||
|
uint16_t hsync_start;
|
||||||
|
uint16_t hsync_end;
|
||||||
|
uint16_t htotal;
|
||||||
|
uint16_t hskew;
|
||||||
|
uint16_t vdisplay;
|
||||||
|
uint16_t vsync_start;
|
||||||
|
uint16_t vsync_end;
|
||||||
|
uint16_t vtotal;
|
||||||
|
uint16_t vscan;
|
||||||
|
uint32_t vrefresh;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_set_crtc_wire {
|
||||||
|
uint32_t crtc_id;
|
||||||
|
uint32_t fb_handle;
|
||||||
|
uint32_t connector_count;
|
||||||
|
uint32_t connectors[8];
|
||||||
|
struct redox_drm_mode_wire mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_page_flip_wire {
|
||||||
|
uint32_t crtc_id;
|
||||||
|
uint32_t fb_handle;
|
||||||
|
uint32_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_create_dumb_wire {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t bpp;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t pitch;
|
||||||
|
uint32_t reserved0;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t reserved1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_map_dumb_wire {
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t pad;
|
||||||
|
uint64_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_destroy_dumb_wire {
|
||||||
|
uint32_t handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_add_fb_wire {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t pitch;
|
||||||
|
uint32_t bpp;
|
||||||
|
uint32_t depth;
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t fb_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_rm_fb_wire {
|
||||||
|
uint32_t fb_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_get_crtc_wire {
|
||||||
|
uint32_t crtc_id;
|
||||||
|
uint32_t fb_id;
|
||||||
|
uint32_t x;
|
||||||
|
uint32_t y;
|
||||||
|
uint32_t mode_valid;
|
||||||
|
struct redox_drm_mode_wire mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_version_wire {
|
||||||
|
int32_t major;
|
||||||
|
int32_t minor;
|
||||||
|
int32_t patch;
|
||||||
|
char name[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_prime_handle_to_fd_wire {
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_prime_handle_to_fd_response_wire {
|
||||||
|
int32_t fd;
|
||||||
|
uint32_t pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_prime_fd_to_handle_wire {
|
||||||
|
int32_t fd;
|
||||||
|
uint32_t pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct redox_drm_prime_fd_to_handle_response_wire {
|
||||||
|
uint32_t handle;
|
||||||
|
uint32_t pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned long redox_translate_request(unsigned long linux_nr);
|
||||||
|
int redox_drm_simple_ioctl(int fd, unsigned long request, void *arg);
|
||||||
|
int redox_drm_exchange(int fd, unsigned long request_code,
|
||||||
|
const void *payload, size_t payload_size,
|
||||||
|
void *response, size_t response_capacity,
|
||||||
|
size_t *response_size);
|
||||||
|
int redox_drm_exchange_alloc(int fd, unsigned long request_code,
|
||||||
|
const void *payload, size_t payload_size,
|
||||||
|
void **response, size_t *response_size);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[source]
|
||||||
|
path = "source"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
dependencies = [
|
||||||
|
"mesa",
|
||||||
|
]
|
||||||
|
script = """
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
cookbook_meson \
|
||||||
|
-Ddocs=false \
|
||||||
|
-Dtests=false \
|
||||||
|
-Dglx=no \
|
||||||
|
-Dx11=false \
|
||||||
|
-Degl=yes
|
||||||
|
"""
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[source]
|
||||||
|
path = "source"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
template = "custom"
|
||||||
|
script = """
|
||||||
|
DYNAMIC_INIT
|
||||||
|
|
||||||
|
cookbook_meson
|
||||||
|
"""
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../local/patches/qtbase/futex-redox-support.patch
|
||||||
@@ -4,9 +4,11 @@
|
|||||||
# Redox platform detection and syscall adaptations in redox.patch
|
# Redox platform detection and syscall adaptations in redox.patch
|
||||||
[source]
|
[source]
|
||||||
tar = "https://download.qt.io/official_releases/qt/6.8/6.8.2/submodules/qtbase-everywhere-src-6.8.2.tar.xz"
|
tar = "https://download.qt.io/official_releases/qt/6.8/6.8.2/submodules/qtbase-everywhere-src-6.8.2.tar.xz"
|
||||||
|
blake3 = "6e9a81b44a2f6a12ce36b77a990a1e18586afe2ab2b140113b4ec59c6ba5d3c6"
|
||||||
|
# TODO: qtwayland-empty-cursor-guards and qtwaylandscanner-null-guard-listeners
|
||||||
|
# were for Qt 6.8.2; changes applied directly to v6.11-patched source tree.
|
||||||
|
# Regenerate proper patches for 6.11.0 and restore to this list.
|
||||||
patches = [
|
patches = [
|
||||||
"qtwaylandscanner-null-guard-listeners.patch",
|
|
||||||
"qtwayland-empty-cursor-guards.patch",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
@@ -650,10 +652,10 @@ fi
|
|||||||
cmake "${COOKBOOK_SOURCE}" \
|
cmake "${COOKBOOK_SOURCE}" \
|
||||||
-GNinja \
|
-GNinja \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
|
||||||
-DCMAKE_SHARED_LINKER_FLAGS="-lc -lffi -lredbear-qt-strtold-compat" \
|
-DCMAKE_SHARED_LINKER_FLAGS="-lc -lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-lc -lffi -lredbear-qt-strtold-compat" \
|
-DCMAKE_EXE_LINKER_FLAGS="-lc -lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||||
-DCMAKE_C_STANDARD_LIBRARIES="-lffi -lredbear-qt-strtold-compat" \
|
-DCMAKE_C_STANDARD_LIBRARIES="-lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||||
-DCMAKE_CXX_STANDARD_LIBRARIES="-lffi -lredbear-qt-strtold-compat" \
|
-DCMAKE_CXX_STANDARD_LIBRARIES="-lffi -lgcc -lredbear-qt-strtold-compat" \
|
||||||
-DQT_HOST_PATH="${HOST_BUILD}" \
|
-DQT_HOST_PATH="${HOST_BUILD}" \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
|||||||
@@ -275,12 +275,4 @@ redbear_qt_copy_stage_qt6_cmake_to_sysroot "${STAGE_USR}" "${SYSROOT}"
|
|||||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes
|
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" metatypes
|
||||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" plugins
|
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" plugins
|
||||||
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" qml
|
redbear_qt_copy_optional_stage_dir_to_sysroot "${STAGE_USR}" "${SYSROOT}" qml
|
||||||
|
|
||||||
for stdlib in \
|
|
||||||
"${COOKBOOK_STAGE}/usr/include/stdlib.h" \
|
|
||||||
"${SYSROOT}/include/stdlib.h" \
|
|
||||||
"${SYSROOT}/usr/include/stdlib.h"; do
|
|
||||||
[ -f "$stdlib" ] || continue
|
|
||||||
sed -i '/strtold[[:space:]]*(/d' "$stdlib" 2>/dev/null || true
|
|
||||||
done
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ impl Driver for DriverConfig {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !std::path::Path::new(&actual_path).exists() {
|
if !std::path::Path::new(&actual_path).exists() {
|
||||||
return ProbeResult::Deferred {
|
return ProbeResult::Fatal {
|
||||||
reason: format!("driver binary not found: {}", actual_path),
|
reason: format!("driver binary not found: {}", actual_path),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::BTreeSet;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -10,6 +10,11 @@ use redox_driver_core::manager::ProbeEvent;
|
|||||||
|
|
||||||
use crate::scheme::{DriverManagerScheme, notify_bind, notify_unbind};
|
use crate::scheme::{DriverManagerScheme, notify_bind, notify_unbind};
|
||||||
|
|
||||||
|
/// Maximum times a single deferred device+driver pair is retried before
|
||||||
|
/// permanently abandoning it. Deferred means a dependency scheme (e.g.
|
||||||
|
/// scheme:firmware) is not yet ready, so the device may bind later.
|
||||||
|
const MAX_DEFERRED_RETRIES: u32 = 5;
|
||||||
|
|
||||||
pub fn run_hotplug_loop(
|
pub fn run_hotplug_loop(
|
||||||
manager: Arc<Mutex<DeviceManager>>,
|
manager: Arc<Mutex<DeviceManager>>,
|
||||||
scheme: Arc<DriverManagerScheme>,
|
scheme: Arc<DriverManagerScheme>,
|
||||||
@@ -20,6 +25,9 @@ pub fn run_hotplug_loop(
|
|||||||
poll_interval_ms
|
poll_interval_ms
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut deferred_retries: BTreeMap<(String, String), u32> = BTreeMap::new();
|
||||||
|
let mut permanently_fatal: BTreeSet<(String, String)> = BTreeSet::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
thread::sleep(Duration::from_millis(poll_interval_ms));
|
thread::sleep(Duration::from_millis(poll_interval_ms));
|
||||||
|
|
||||||
@@ -57,18 +65,41 @@ pub fn run_hotplug_loop(
|
|||||||
result,
|
result,
|
||||||
} => {
|
} => {
|
||||||
track_pci_device(device, &mut seen_pci_devices);
|
track_pci_device(device, &mut seen_pci_devices);
|
||||||
|
let key = (device.path.clone(), driver_name.clone());
|
||||||
|
|
||||||
|
// Skip devices that were permanently fatal in a previous cycle.
|
||||||
|
// enumerate() re-probes all unbound devices each poll, but a Fatal
|
||||||
|
// result means the driver binary is genuinely absent (e.g. ided on
|
||||||
|
// a live ISO that doesn't ship it) — no amount of re-probing will
|
||||||
|
// change the outcome.
|
||||||
|
if permanently_fatal.contains(&key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
ProbeResult::Bound => {
|
ProbeResult::Bound => {
|
||||||
log::info!("hotplug: bound {} -> {}", device.path, driver_name);
|
log::info!("hotplug: bound {} -> {}", device.path, driver_name);
|
||||||
notify_bound_device(scheme.as_ref(), device, driver_name);
|
notify_bound_device(scheme.as_ref(), device, driver_name);
|
||||||
}
|
}
|
||||||
ProbeResult::Deferred { reason } => {
|
ProbeResult::Deferred { reason } => {
|
||||||
log::info!(
|
let retries = deferred_retries.entry(key).or_insert(0);
|
||||||
"hotplug: deferred {} -> {} ({})",
|
*retries += 1;
|
||||||
device.path,
|
if *retries <= MAX_DEFERRED_RETRIES {
|
||||||
driver_name,
|
log::info!(
|
||||||
reason
|
"hotplug: deferred {} -> {} ({})",
|
||||||
);
|
device.path,
|
||||||
|
driver_name,
|
||||||
|
reason
|
||||||
|
);
|
||||||
|
} else if *retries == MAX_DEFERRED_RETRIES + 1 {
|
||||||
|
log::warn!(
|
||||||
|
"hotplug: giving up on {} -> {} after {} retries: {}",
|
||||||
|
device.path,
|
||||||
|
driver_name,
|
||||||
|
MAX_DEFERRED_RETRIES,
|
||||||
|
reason
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ProbeResult::Fatal { reason } => {
|
ProbeResult::Fatal { reason } => {
|
||||||
log::error!(
|
log::error!(
|
||||||
@@ -77,6 +108,7 @@ pub fn run_hotplug_loop(
|
|||||||
driver_name,
|
driver_name,
|
||||||
reason
|
reason
|
||||||
);
|
);
|
||||||
|
permanently_fatal.insert(key);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
export DISPLAY=""
|
# NOTE: Neither WAYLAND_DISPLAY nor DISPLAY may be exported when starting
|
||||||
export WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-0}"
|
# KWin. KWin uses their presence (even as empty strings) to select the
|
||||||
|
# nested Wayland or X11 backend instead of the bare-metal DRM/KMS backend.
|
||||||
|
# Clients (the greeter UI, KDE session apps) receive WAYLAND_DISPLAY from
|
||||||
|
# redbear-session-launch instead.
|
||||||
|
unset DISPLAY
|
||||||
|
unset WAYLAND_DISPLAY
|
||||||
export XDG_SESSION_TYPE=wayland
|
export XDG_SESSION_TYPE=wayland
|
||||||
export LIBSEAT_BACKEND=seatd
|
export LIBSEAT_BACKEND=seatd
|
||||||
export SEATD_SOCK=/run/seatd.sock
|
export SEATD_SOCK=/run/seatd.sock
|
||||||
@@ -13,6 +18,10 @@ export QML2_IMPORT_PATH="${QML2_IMPORT_PATH:-/usr/qml}"
|
|||||||
export QT_WAYLAND_SHELL_INTEGRATION="${QT_WAYLAND_SHELL_INTEGRATION:-xdg-shell}"
|
export QT_WAYLAND_SHELL_INTEGRATION="${QT_WAYLAND_SHELL_INTEGRATION:-xdg-shell}"
|
||||||
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
|
export XCURSOR_THEME="${XCURSOR_THEME:-Pop}"
|
||||||
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
||||||
|
export KWIN_COMPOSE="${KWIN_COMPOSE:-O2}"
|
||||||
|
export QT_DEBUG_PLUGINS=1
|
||||||
|
export QT_LOGGING_RULES="${QT_LOGGING_RULES:-*.debug=true}"
|
||||||
|
export MESA_LOADER_DRIVER_OVERRIDE="${MESA_LOADER_DRIVER_OVERRIDE:-virtio_gpu}"
|
||||||
|
|
||||||
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
||||||
export XDG_RUNTIME_DIR="/tmp/run/redbear-greeter"
|
export XDG_RUNTIME_DIR="/tmp/run/redbear-greeter"
|
||||||
@@ -20,53 +29,27 @@ fi
|
|||||||
|
|
||||||
mkdir -p "$XDG_RUNTIME_DIR"
|
mkdir -p "$XDG_RUNTIME_DIR"
|
||||||
|
|
||||||
parse_drm_devices() {
|
drm_scheme_ready() {
|
||||||
local devices="${1:-}"
|
# Try to open /scheme/drm/card0 via read (head -c 1).
|
||||||
local part=""
|
# On Redox, stat and test -e are unreliable for scheme paths,
|
||||||
|
# but opening the scheme file descriptor works.
|
||||||
IFS=':' read -r -a parts <<< "$devices"
|
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
|
||||||
for part in "${parts[@]}"; do
|
|
||||||
if [ -n "$part" ]; then
|
|
||||||
printf '%s\n' "$part"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_devices_ready() {
|
wait_for_drm_scheme() {
|
||||||
local devices="${1:-}"
|
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-10}"
|
||||||
local device=""
|
|
||||||
|
|
||||||
for device in $(parse_drm_devices "$devices"); do
|
|
||||||
if [ ! -e "$device" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_drm_devices() {
|
|
||||||
local devices="${1:-}"
|
|
||||||
local wait_seconds="${REDBEAR_DRM_WAIT_SECONDS:-2}"
|
|
||||||
local attempts=0
|
local attempts=0
|
||||||
|
|
||||||
if [ -z "$devices" ]; then
|
if drm_scheme_ready; then
|
||||||
return 1
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$wait_seconds" -le 0 ] 2>/dev/null; then
|
echo "redbear-greeter-compositor: waiting up to ${wait_seconds}s for DRM device(s): /scheme/drm:/scheme/drm/card0" >&2
|
||||||
drm_devices_ready "$devices"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
attempts=$((wait_seconds * 2))
|
attempts=$((wait_seconds * 2))
|
||||||
|
|
||||||
if ! drm_devices_ready "$devices"; then
|
|
||||||
echo "redbear-greeter-compositor: waiting up to ${wait_seconds}s for DRM device(s): ${devices}" >&2
|
|
||||||
fi
|
|
||||||
|
|
||||||
while [ "$attempts" -gt 0 ]; do
|
while [ "$attempts" -gt 0 ]; do
|
||||||
if drm_devices_ready "$devices"; then
|
if drm_scheme_ready; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -74,13 +57,9 @@ wait_for_drm_devices() {
|
|||||||
attempts=$((attempts - 1))
|
attempts=$((attempts - 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
drm_devices_ready "$devices"
|
drm_scheme_ready
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ] && command -v dbus-launch >/dev/null 2>&1; then
|
|
||||||
eval "$(dbus-launch --sh-syntax)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prefer kwin_wayland (real KWin compositor). Fall back to redbear-compositor only if
|
# Prefer kwin_wayland (real KWin compositor). Fall back to redbear-compositor only if
|
||||||
# KWin is not installed (e.g. text-only target accidentally includes greeter).
|
# KWin is not installed (e.g. text-only target accidentally includes greeter).
|
||||||
COMPOSITOR=""
|
COMPOSITOR=""
|
||||||
@@ -97,27 +76,28 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# pcid-spawner is intentionally async in Red Bear OS, so service ordering only guarantees that
|
# pcid-spawner is intentionally async in Red Bear OS, so service ordering only guarantees that
|
||||||
# spawning has started. Wait for the DRM scheme root first, then the selected card node.
|
# spawning has started. Wait for the DRM scheme to appear.
|
||||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
if wait_for_drm_scheme; then
|
||||||
desired_drm_devices="/scheme/drm:${KWIN_DRM_DEVICES}"
|
|
||||||
else
|
|
||||||
desired_drm_devices="/scheme/drm:/scheme/drm/card0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if wait_for_drm_devices "$desired_drm_devices"; then
|
|
||||||
export KWIN_DRM_DEVICES="${KWIN_DRM_DEVICES:-/scheme/drm/card0}"
|
export KWIN_DRM_DEVICES="${KWIN_DRM_DEVICES:-/scheme/drm/card0}"
|
||||||
echo "redbear-greeter-compositor: using DRM compositor backend (KWIN_DRM_DEVICES=${KWIN_DRM_DEVICES})" >&2
|
echo "redbear-greeter-compositor: using DRM compositor backend (KWIN_DRM_DEVICES=${KWIN_DRM_DEVICES})" >&2
|
||||||
if [ "$COMPOSITOR" = "kwin_wayland" ] || [ "$COMPOSITOR" = "/usr/bin/kwin_wayland" ]; then
|
|
||||||
exec "$COMPOSITOR" --drm
|
# NOTE: QDBusConnection::sessionBus() blocks on Redox when attempting Unix socket
|
||||||
else
|
# connect(), even when dbus-daemon is running. All KWin sessionBus() calls in the
|
||||||
exec "$COMPOSITOR" --drm
|
# startup path have been bypassed in the KWin source. We do NOT set
|
||||||
fi
|
# DBUS_SESSION_BUS_ADDRESS so that remaining sessionBus() calls with env-var guards
|
||||||
else
|
# skip gracefully. If a future change re-enables D-Bus session bus, the Qt/Redox
|
||||||
unset KWIN_DRM_DEVICES
|
# Unix socket connect() issue must be resolved first.
|
||||||
echo "redbear-greeter-compositor: DRM device not ready after wait, using virtual compositor backend" >&2
|
|
||||||
if [ "$COMPOSITOR" = "kwin_wayland" ] || [ "$COMPOSITOR" = "/usr/bin/kwin_wayland" ]; then
|
echo "redbear-greeter-compositor: env DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-unset} DBUS_SYSTEM_BUS_ADDRESS=${DBUS_SYSTEM_BUS_ADDRESS:-unset}" >&2
|
||||||
exec "$COMPOSITOR" --virtual
|
echo "redbear-greeter-compositor: launching $COMPOSITOR" >&2
|
||||||
else
|
QT_QPA_PLATFORM=offscreen "$COMPOSITOR"
|
||||||
exec "$COMPOSITOR" --virtual
|
EXIT_CODE=$?
|
||||||
fi
|
echo "redbear-greeter-compositor: compositor exited with code $EXIT_CODE" >&2
|
||||||
|
exit $EXIT_CODE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# No DRM at all — fall back to virtual backend.
|
||||||
|
unset KWIN_DRM_DEVICES
|
||||||
|
echo "redbear-greeter-compositor: no DRM device found, using virtual compositor backend" >&2
|
||||||
|
"$COMPOSITOR"
|
||||||
|
exit $?
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-KDE}"
|
|||||||
export XDG_SESSION_ID="${XDG_SESSION_ID:-c1}"
|
export XDG_SESSION_ID="${XDG_SESSION_ID:-c1}"
|
||||||
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
||||||
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
export XKB_CONFIG_ROOT="${XKB_CONFIG_ROOT:-/usr/share/X11/xkb}"
|
||||||
|
export KWIN_COMPOSE="${KWIN_COMPOSE:-Q}"
|
||||||
|
|
||||||
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
|
||||||
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
|
export XDG_RUNTIME_DIR="/tmp/run/user/$(id -u)"
|
||||||
@@ -89,12 +90,18 @@ trap cleanup EXIT INT TERM
|
|||||||
|
|
||||||
kwin_mode="virtual"
|
kwin_mode="virtual"
|
||||||
|
|
||||||
|
# Redox scheme paths do not respond to stat()/test -e, but opening and
|
||||||
|
# reading a byte works. Use the same probe the compositor wrapper uses.
|
||||||
|
drm_scheme_ready() {
|
||||||
|
( head -c 1 "/scheme/drm/card0" ) >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
set_kwin_mode() {
|
set_kwin_mode() {
|
||||||
local requested="${REDBEAR_KDE_SESSION_BACKEND:-auto}"
|
local requested="${REDBEAR_KDE_SESSION_BACKEND:-auto}"
|
||||||
|
|
||||||
case "$requested" in
|
case "$requested" in
|
||||||
drm)
|
drm)
|
||||||
if [ -z "${KWIN_DRM_DEVICES:-}" ] && [ -e /scheme/drm/card0 ]; then
|
if [ -z "${KWIN_DRM_DEVICES:-}" ] && drm_scheme_ready; then
|
||||||
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
||||||
fi
|
fi
|
||||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
||||||
@@ -109,7 +116,7 @@ set_kwin_mode() {
|
|||||||
auto|"")
|
auto|"")
|
||||||
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
if [ -n "${KWIN_DRM_DEVICES:-}" ]; then
|
||||||
kwin_mode="drm"
|
kwin_mode="drm"
|
||||||
elif [ -e /scheme/drm/card0 ]; then
|
elif drm_scheme_ready; then
|
||||||
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
export KWIN_DRM_DEVICES=/scheme/drm/card0
|
||||||
kwin_mode="drm"
|
kwin_mode="drm"
|
||||||
else
|
else
|
||||||
@@ -257,6 +264,11 @@ kwin_pid=$!
|
|||||||
|
|
||||||
if ! wait_for_wayland_socket; then
|
if ! wait_for_wayland_socket; then
|
||||||
printf '%s\n' "redbear-kde-session: $COMPOSITOR failed to expose $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" >&2
|
printf '%s\n' "redbear-kde-session: $COMPOSITOR failed to expose $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" >&2
|
||||||
|
printf '%s\n' "redbear-kde-session: kwin_pid=$kwin_pid, kwin_alive=$(kill -0 $kwin_pid 2>/dev/null && echo yes || echo no)" >&2
|
||||||
|
printf '%s\n' "redbear-kde-session: XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" >&2
|
||||||
|
printf '%s\n' "redbear-kde-session: WAYLAND_DISPLAY=$WAYLAND_DISPLAY" >&2
|
||||||
|
printf '%s\n' "redbear-kde-session: contents of $XDG_RUNTIME_DIR:" >&2
|
||||||
|
ls -la "$XDG_RUNTIME_DIR" 2>/dev/null >&2 || echo "(dir not found)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ patches = [
|
|||||||
"P4-acpi-shutdown-hardening.patch",
|
"P4-acpi-shutdown-hardening.patch",
|
||||||
"P4-acpi-s3-sleep.patch",
|
"P4-acpi-s3-sleep.patch",
|
||||||
"P4-pcid-public-client-channel.patch",
|
"P4-pcid-public-client-channel.patch",
|
||||||
|
"P4-pcid-config-scheme.patch",
|
||||||
"P4-pcid-spawner-pci-coordinate-env.patch",
|
"P4-pcid-spawner-pci-coordinate-env.patch",
|
||||||
"P4-initfs-usb-drm-services.patch",
|
"P4-initfs-usb-drm-services.patch",
|
||||||
"P4-initfs-release-virtio-gpu.patch",
|
"P4-initfs-release-virtio-gpu.patch",
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ patches = [
|
|||||||
"P3-ipc-tests.patch",
|
"P3-ipc-tests.patch",
|
||||||
# ST_RDONLY/ST_NOSUID constants for statvfs (POSIX)
|
# ST_RDONLY/ST_NOSUID constants for statvfs (POSIX)
|
||||||
"P3-statvfs-constants.patch",
|
"P3-statvfs-constants.patch",
|
||||||
|
"P3-ld-so-usr-lib-search-path.patch",
|
||||||
|
# Remove relibc's DRM ioctl handler — libdrm's redox_drm_simple_ioctl()
|
||||||
|
# handles all DRM ioctls with its own scheme:drm wire format.
|
||||||
|
"P5-remove-drm-ioctl-intercept.patch",
|
||||||
]
|
]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ dependencies = [
|
|||||||
"zlib"
|
"zlib"
|
||||||
]
|
]
|
||||||
script = """
|
script = """
|
||||||
DYNAMIC_STATIC_INIT
|
DYNAMIC_INIT
|
||||||
cookbook_meson
|
cookbook_meson -Dpng=disabled
|
||||||
"""
|
"""
|
||||||
|
|||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../../local/recipes/system/dbus
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
qemu-system-x86_64 -m 8G -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive file=/home/kellito/Builds/rbos/build/x86_64/redbear-full.iso,format=raw -device virtio-gpu-pci -serial mon:stdio
|
qemu-system-x86_64 -m 8G -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd -drive file=/home/kellito/Builds/rbos/build/x86_64/redbear-full.iso,format=raw -device virtio-gpu-pci -enable-kvm -serial mon:stdio
|
||||||
|
|||||||
Reference in New Issue
Block a user