#!/bin/sh
### BEGIN INIT INFO
# Provides:          vdev-coldplug
# Required-Start:    mountkernfs
# Required-Stop:
# Default-Start:     S
# Default-Stop:
# Short-Description: KMS fallback and background coldplug worker
### END INIT INFO

PATH="/sbin:/bin:/usr/bin"

_json="${VDEV_GLOBALS_JSON:-/etc/vdev/vdevd.globals.json}"

if [ -r "$_json" ] && command -v jq >/dev/null 2>&1; then
	VDEV_JSON_DATA="$(sed -E 's|//.*||g' "$_json" |
		sed 's|/\*.*\*/||g' |
		grep -v '^$' |
		jq -r .)"
	export VDEV_JSON_DATA
else
	VDEV_CONFIG_DIR="/etc/vdev"
	VDEV_LONG_RUNNING="vdevd.conf"
	VDEV_STAMP_DIR="/run/vdev"
	VDEV_READY_STAMP="/run/vdev/vdev.ready"
	VDEV_EARLY_SUBSYSTEMS="graphics video4linux input hid i2c drm fd"
fi

get_json_get_key() {
	printf '%s' "$VDEV_JSON_DATA" | jq -r --arg k "$1" '
		.globals[$k] // empty' 2>/dev/null
}

get_json_get_config() {
	printf '%s' "$VDEV_JSON_DATA" | jq -r --arg s "$1" '
		.profiles[$s].config // .states[$s].config // empty' 2>/dev/null
}

get_json_get_stamp() {
	printf '%s' "$VDEV_JSON_DATA" | jq -r --arg s "$1" '
		.profiles[$s].stamp // .states[$s].stamp // empty' 2>/dev/null
}

get_json_get_subsystems() {
	printf '%s' "$VDEV_JSON_DATA" | jq -r --arg s "$1" '
		(.profiles[$s].subsystems // .states[$s].subsystems // []) | .[]' 2>/dev/null
}

if [ -n "$VDEV_JSON_DATA" ]; then
	_config_dir="$(get_json_get_key "config-dir")"
	VDEV_CONFIG_DIR="${_config_dir:-/etc/vdev}"

	_long_running="$(get_json_get_config "long-running")"
	VDEV_LONG_RUNNING="${_long_running:-vdevd.conf}"

	_stamp_dir="$(get_json_get_key "states-stamp-dir")"
	VDEV_STAMP_DIR="${_stamp_dir:-/run/vdev}"

	_stamp_early="$(get_json_get_stamp "early")"
	VDEV_READY_STAMP="${VDEV_STAMP_DIR}/${_stamp_early:-vdev.ready}"

	_early_subs="$(get_json_get_subsystems "early" | tr '\n' ' ')"
	VDEV_EARLY_SUBSYSTEMS="${_early_subs:-graphics video4linux input hid i2c drm fd}"
fi

VDEV_LONG_RUNNING_CONFIG="$VDEV_CONFIG_DIR/$VDEV_LONG_RUNNING"
: "${VDEV_UDEV_CONTROL:=/run/udev/control}"

. /lib/lsb/init-functions

vdev_wait_for_stamp() {
	_vs_path="$1"
	_vs_max="${2:-250}"
	_vs_sleep="${3:-0.2}"
	_vs_i=0
	while [ "$_vs_i" -lt "$_vs_max" ]; do
		[ -e "$_vs_path" ] && return 0
		sleep "$_vs_sleep"
		_vs_i=$((_vs_i + 1))
	done
	return 1
}

load_boot_vga_kms() {
	for boot_file in /sys/bus/pci/devices/*/boot_vga; do
		[ -f "$boot_file" ] || continue
		[ "$(cat "$boot_file")" = "1" ] || continue
		pci_dir=$(dirname "$boot_file")
		[ -f "$pci_dir/modalias" ] || continue
		alias_string=$(cat "$pci_dir/modalias")
		case "$alias_string" in
			*v00008086*|*v000010DE*)
				# Intel / NVIDIA: load from modalias (i915 / nouveau).
				modprobe -q "$alias_string" 2>/dev/null || true
				;;
			*v00001002*)
				# AMD: skip amdgpu here — can load after X; often noisy /
				# slow when proprietary firmware is missing. Initrd only.
				;;
			*)
				[ -n "$alias_string" ] && modprobe -q nouveau 2>/dev/null || true
				;;
		esac
		break
	done
}

vdev_promote_to_long_running() {
	_try=0
	_max="${VDEV_PROMOTE_RETRIES:-5}"
	_cfg="${VDEV_LONG_RUNNING_CONFIG:-}"

	while [ "$_try" -lt "$_max" ]; do
		_try=$((_try + 1))
		if [ -n "$_cfg" ]; then
			vdevadm control --promote="$_cfg" >/dev/null 2>&1 && return 0
		else
			vdevadm control --promote >/dev/null 2>&1 && return 0
		fi
		sleep 0.1
	done
	return 1
}

vdev_promote_and_touch_ready() {
	vdev_promote_to_long_running || true
	touch "$VDEV_READY_STAMP"
}

vdev_coldplug_worker() {
	_s=""

	vdev_wait_for_stamp "$VDEV_UDEV_CONTROL" 250 0.1 || return 0

	for _s in $VDEV_EARLY_SUBSYSTEMS; do
		vdevadm trigger -s "$_s" -a add >/dev/null 2>&1 || true
	done
	vdevadm settle -t "${VDEV_EARLY_SETTLE_TIMEOUT:-15}" >/dev/null 2>&1 || true

	vdev_promote_and_touch_ready

	vdevadm trigger --sysfs -a add >/dev/null 2>&1 || true
}

case "$1" in
start)
	load_boot_vga_kms
	log_daemon_msg "Starting coldplug worker in background" "vdev-coldplug"
	( vdev_coldplug_worker ) >/dev/null 2>&1 &
	log_end_msg 0
	;;
stop|restart|force-reload|status)
	;;
*)
	echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
	exit 1
	;;
esac
