初始化奇妙小屏幕控制器项目
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
from typing import Callable, Sequence
|
||||
|
||||
|
||||
EXPECTED_PINCTRL = "allwinner,sun50i-h616-pinctrl"
|
||||
DISABLED_SERVICES = (
|
||||
"lightdm.service",
|
||||
"bluetooth.service",
|
||||
"aw859-bluetooth.service",
|
||||
"gpioc-server.service",
|
||||
"map_device.service",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CommandResult:
|
||||
returncode: int
|
||||
stdout: str = ""
|
||||
stderr: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HostPaths:
|
||||
pinctrl_compatible: Path
|
||||
dev_mem: Path
|
||||
i2c_device: Path
|
||||
cpu3: Path
|
||||
native_library: Path
|
||||
|
||||
@classmethod
|
||||
def detect(
|
||||
cls,
|
||||
root: Path = Path("/"),
|
||||
source_root: Path | None = None,
|
||||
) -> "HostPaths":
|
||||
source_root = source_root or Path(__file__).resolve().parents[1]
|
||||
compatible = (
|
||||
root
|
||||
/ "proc"
|
||||
/ "device-tree"
|
||||
/ "soc"
|
||||
/ "pinctrl@300b000"
|
||||
/ "compatible"
|
||||
)
|
||||
return cls(
|
||||
pinctrl_compatible=compatible,
|
||||
dev_mem=root / "dev" / "mem",
|
||||
i2c_device=root / "dev" / "i2c-1",
|
||||
cpu3=root / "sys" / "devices" / "system" / "cpu" / "cpu3",
|
||||
native_library=(
|
||||
source_root / "app" / "display" / "native" / "libh618_hub75.so"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HostCheck:
|
||||
name: str
|
||||
ok: bool
|
||||
detail: str
|
||||
|
||||
|
||||
Runner = Callable[[Sequence[str]], CommandResult]
|
||||
|
||||
|
||||
def run_command(args: Sequence[str]) -> CommandResult:
|
||||
completed = subprocess.run(
|
||||
list(args),
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return CommandResult(completed.returncode, completed.stdout, completed.stderr)
|
||||
|
||||
|
||||
def _compatible_values(path: Path) -> tuple[str, ...]:
|
||||
if not path.is_file():
|
||||
return ()
|
||||
return tuple(
|
||||
value.decode("ascii", errors="replace")
|
||||
for value in path.read_bytes().split(b"\0")
|
||||
if value
|
||||
)
|
||||
|
||||
|
||||
def check_profile(
|
||||
paths: HostPaths,
|
||||
*,
|
||||
service_only: bool = False,
|
||||
runner: Runner = run_command,
|
||||
) -> list[HostCheck]:
|
||||
compatible = _compatible_values(paths.pinctrl_compatible)
|
||||
architecture = runner(("uname", "-m"))
|
||||
network_manager = runner(("systemctl", "is-active", "NetworkManager.service"))
|
||||
|
||||
checks = [
|
||||
HostCheck(
|
||||
"H618 PI pinctrl",
|
||||
EXPECTED_PINCTRL in compatible,
|
||||
", ".join(compatible) if compatible else "compatible node missing",
|
||||
),
|
||||
HostCheck(
|
||||
"/dev/mem accessible",
|
||||
paths.dev_mem.exists()
|
||||
and os.access(paths.dev_mem, os.R_OK | os.W_OK),
|
||||
str(paths.dev_mem),
|
||||
),
|
||||
HostCheck(
|
||||
"/dev/i2c-1 present",
|
||||
paths.i2c_device.exists(),
|
||||
str(paths.i2c_device),
|
||||
),
|
||||
HostCheck("CPU3 present", paths.cpu3.is_dir(), str(paths.cpu3)),
|
||||
HostCheck(
|
||||
"native HUB75 library",
|
||||
paths.native_library.is_file(),
|
||||
str(paths.native_library),
|
||||
),
|
||||
HostCheck(
|
||||
"AArch64 userspace",
|
||||
architecture.returncode == 0 and architecture.stdout.strip() == "aarch64",
|
||||
architecture.stdout.strip() or architecture.stderr.strip(),
|
||||
),
|
||||
HostCheck(
|
||||
"NetworkManager active",
|
||||
network_manager.returncode == 0
|
||||
and network_manager.stdout.strip() == "active",
|
||||
network_manager.stdout.strip() or network_manager.stderr.strip(),
|
||||
),
|
||||
]
|
||||
if not service_only:
|
||||
route = runner(("ip", "route", "show", "default"))
|
||||
checks.insert(
|
||||
-1,
|
||||
HostCheck(
|
||||
"default network route",
|
||||
route.returncode == 0 and bool(route.stdout.strip()),
|
||||
route.stdout.strip() or route.stderr.strip(),
|
||||
),
|
||||
)
|
||||
for unit in DISABLED_SERVICES:
|
||||
state = runner(("systemctl", "is-active", unit))
|
||||
checks.append(
|
||||
HostCheck(
|
||||
f"{unit} inactive",
|
||||
state.stdout.strip() != "active",
|
||||
state.stdout.strip() or state.stderr.strip() or "inactive",
|
||||
)
|
||||
)
|
||||
return checks
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Verify the dedicated WalnutPi ZeroW/H618 host profile."
|
||||
)
|
||||
parser.add_argument("command", choices=("check",))
|
||||
parser.add_argument("--service", action="store_true")
|
||||
parser.add_argument("--json", action="store_true")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
checks = check_profile(HostPaths.detect(), service_only=args.service)
|
||||
if args.json:
|
||||
print(json.dumps([asdict(check) for check in checks], ensure_ascii=False))
|
||||
else:
|
||||
for check in checks:
|
||||
print(f"{'PASS' if check.ok else 'FAIL'}: {check.name}: {check.detail}")
|
||||
return 0 if all(check.ok for check in checks) else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user