#!/bin/sh
# Clawdie operator USB live GPU setup.
# Detect display hardware and load a conservative KMS module before SDDM.
# Prefer integrated/open drivers so hybrid laptops boot the broadest path first;
# keep NVIDIA proprietary loading opt-in by requiring an installed nvidia.ko.
# Dedicated NVIDIA-target images can override that preference in rc.conf.

# PROVIDE: clawdie_live_gpu
# REQUIRE: FILESYSTEMS devfs
# BEFORE: sddm
# KEYWORD: nojail

. /etc/rc.subr

name="clawdie_live_gpu"
rcvar="clawdie_live_gpu_enable"
start_cmd="clawdie_live_gpu_start"

LOG_FILE="/var/log/clawdie-live-gpu.log"

clawdie_live_gpu_log()
{
    echo "$(date '+%H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true
}

clawdie_live_gpu_display_blocks()
{
    pciconf -lv 2>/dev/null | awk '
        BEGIN { RS = ""; IGNORECASE = 1 }
        /class=0x03/ { print }
    '
}

clawdie_live_gpu_nvidia_block()
{
    echo "$1" | awk '
        BEGIN { RS = ""; IGNORECASE = 1 }
        /(^|[[:space:]])vendor=0x10de([[:space:]]|$)/ { print; exit }
    '
}

clawdie_live_gpu_nvidia_device_id()
{
    _block=$(clawdie_live_gpu_nvidia_block "$1")
    [ -n "$_block" ] || return 1

    # FreeBSD pciconf chip field is chip=0x<device><vendor>. NVIDIA's vendor id
    # 0x10de sits in the LOW 16 bits, so the device id is the 4 hex digits BEFORE
    # it (e.g. chip=0x1c8c10de -> device id 1c8c). The earlier "chip=0x10de..."
    # form read the vendor half and never matched.
    echo "$_block" | grep -Eo 'chip=0x[0-9a-fA-F]{4}10de' | head -1 |
        sed 's/^chip=0x//; s/10de$//'
}

# Map an NVIDIA PCI device id (4 hex digits) to the recommended driver branch.
# Returns labels in the build's lane vocabulary {390, 470, 590} so the value can
# be compared directly with clawdie_live_gpu_nvidia_branch staged from build.sh.
# (The "590" lane installs the current nvidia-driver-580 package.)
#
# Coarse architecture heuristic by device-id range (validate against real
# hardware; refine the boundaries as needed):
#   Fermi    (       < 0x0f00) -> 390
#   Kepler   (0x0f00 .. 0x12ff) -> 470
#   Maxwell+ (      >= 0x1300) -> 590  (current: Maxwell/Pascal/Turing/Ampere/Ada/...)
# Unknown/empty -> 590, the safest default for modern unknown hardware.
clawdie_live_gpu_nvidia_branch_for_device()
{
    _device_id="$1"
    [ -n "$_device_id" ] || { echo "590"; return 0; }

    _device_num=$(printf '%d' "0x$_device_id" 2>/dev/null || echo "0")

    if [ "$_device_num" -ge 4864 ]; then
        echo "590"        # >= 0x1300  Maxwell, Pascal, Turing, Ampere, Ada, ...
    elif [ "$_device_num" -ge 3840 ]; then
        echo "470"        # 0x0f00 .. 0x12ff  Kepler
    else
        echo "390"        # < 0x0f00  Fermi
    fi
}

clawdie_live_gpu_has_pci_vendor()
{
    _blocks="$1"
    _vendor_id="$2"

    # Match the numeric PCI vendor on display-class blocks only. Do not use
    # broad text matches like "Intel" here: unrelated vendor strings or
    # subdevices can make an AMD system select i915kms and fail before SDDM.
    echo "$_blocks" | awk -v vendor="$_vendor_id" '
        BEGIN { RS = ""; IGNORECASE = 1; found = 0 }
        $0 ~ ("(^|[[:space:]])vendor=" vendor "([[:space:]]|$)") { found = 1 }
        END { exit(found ? 0 : 1) }
    '
}

clawdie_live_gpu_select_modules()
{
    _blocks="$1"
    _mode="${clawdie_live_gpu_mode:-auto}"
    _nvidia_branch="${clawdie_live_gpu_nvidia_branch:-}"

    if [ "$_mode" = "safe" ]; then
        clawdie_live_gpu_log "safe graphics mode requested; skipping KMS modules"
        return 0
    fi

    if clawdie_live_gpu_has_pci_vendor "$_blocks" "0x10de"; then
        _device_id=$(clawdie_live_gpu_nvidia_device_id "$_blocks" || true)
        _detected_branch=""
        if [ -n "$_device_id" ]; then
            _detected_branch=$(clawdie_live_gpu_nvidia_branch_for_device "$_device_id" || true)
            clawdie_live_gpu_log "detected NVIDIA device id: ${_device_id} (recommended branch: ${_detected_branch:-unknown})"
        fi

        if [ -e /boot/modules/nvidia.ko ]; then
            if [ -n "$_nvidia_branch" ] && [ -n "$_detected_branch" ] && [ "$_nvidia_branch" != "$_detected_branch" ]; then
                clawdie_live_gpu_log "WARN: staged NVIDIA branch ${_nvidia_branch} does not match detected device recommendation ${_detected_branch}; skipping proprietary load"
            elif [ "$_mode" = "nvidia" ]; then
                echo "nvidia-modeset nvidia"
                return 0
            fi
        elif [ "$_mode" = "nvidia" ]; then
            clawdie_live_gpu_log "WARN: NVIDIA mode requested but /boot/modules/nvidia.ko is missing"
        fi
    elif [ "$_mode" = "nvidia" ]; then
        clawdie_live_gpu_log "WARN: NVIDIA mode requested but no NVIDIA display device was detected"
    fi

    # Hybrid laptops often expose Intel or AMD integrated graphics plus a
    # discrete NVIDIA GPU. Prefer the integrated/open KMS path for live boot.
    if clawdie_live_gpu_has_pci_vendor "$_blocks" "0x1002"; then
        # amdgpu covers modern AMD; radeonkms is a best-effort fallback for
        # older Radeon hardware only if amdgpu does not load.
        echo "amdgpu radeonkms"
        return 0
    fi

    if clawdie_live_gpu_has_pci_vendor "$_blocks" "0x8086"; then
        echo "i915kms"
        return 0
    fi

    if clawdie_live_gpu_has_pci_vendor "$_blocks" "0x15ad"; then
        echo "vmwgfx"
        return 0
    fi

    if clawdie_live_gpu_has_pci_vendor "$_blocks" "0x10de"; then
        # NVIDIA packages are bundled in the offline repo, but the live image
        # only loads a concrete branch when one was explicitly staged.
        if [ -e /boot/modules/nvidia.ko ]; then
            echo "nvidia-modeset nvidia"
        fi
        return 0
    fi

    return 0
}

clawdie_live_gpu_load_module()
{
    _module="$1"

    if [ "$_module" = "radeonkms" ] && kldstat -n amdgpu.ko >/dev/null 2>&1; then
        clawdie_live_gpu_log "skipping radeonkms because amdgpu is already loaded"
        return 0
    fi

    if kldstat -n "${_module}.ko" >/dev/null 2>&1; then
        clawdie_live_gpu_log "module already loaded: ${_module}"
        return 0
    fi

    if kldload "$_module" >/dev/null 2>&1; then
        clawdie_live_gpu_log "loaded module: ${_module}"
        return 0
    fi

    clawdie_live_gpu_log "WARN: failed to load module: ${_module}"
    return 1
}

clawdie_live_gpu_start()
{
    _blocks=$(clawdie_live_gpu_display_blocks)
    if [ -z "$_blocks" ]; then
        clawdie_live_gpu_log "no display-class PCI device detected; using scfb/vesa fallback"
        return 0
    fi

    clawdie_live_gpu_log "detected display devices: $(echo "$_blocks" | grep -i '^vgapci' | tr '\n' ' ')"
    _modules=$(clawdie_live_gpu_select_modules "$_blocks")
    if [ -z "$_modules" ]; then
        clawdie_live_gpu_log "no live KMS module selected; using scfb/vesa fallback"
        return 0
    fi

    clawdie_live_gpu_log "selected live KMS modules: ${_modules}"
    for _module in $_modules; do
        clawdie_live_gpu_load_module "$_module" || true
    done

    return 0
}

load_rc_config "$name"
: ${clawdie_live_gpu_enable:="NO"}
: ${clawdie_live_gpu_mode:="auto"}
: ${clawdie_live_gpu_nvidia_branch:=""}

run_rc_command "$1"
