#!/usr/sbin/openrc-run

description="Restore and store ALSA driver settings."

_sysfs="/sys"

depend()
{
	need localmount
	# Cards appear after vdev coldplug/settle (sysinit order).
	after vdev-settle-sync.sh bootmisc
}

ALSACTLHOME=/run/alsa
ALSACTLRUNTIME="${ALSACTLHOME}/runtime"

executable() {
	[ -x /bin/which ] || [ -x /usr/bin/which ] || return 1
	which "$1" >/dev/null 2>&1
}

restore_levels() {
	[ -f /var/lib/alsa/asound.state ] || return 1
	local CARD="$1"
	[ "$1" = all ] && CARD=""
	if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" restore $CARD 2>&1 >/dev/null)" && [ ! "$MSG" ]; then
		return 0
	else
		alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" -F restore $CARD >/dev/null 2>&1
		ewarn "alsactl restore failed: $MSG"
		return 1
	fi
}

store_levels() {
	local CARD="$1"
	[ "$1" = all ] && CARD=""
	if MSG="$(alsactl -E HOME="$ALSACTLHOME" -E XDG_RUNTIME_DIR="${ALSACTLRUNTIME}" store $CARD 2>&1)"; then
		sleep 1
		return 0
	else
		ewarn "alsactl store failed: $MSG"
		return 1
	fi
}

card_OK() {
	[ "$1" ] || return 1
	if [ "$1" = all ]; then [ -d /proc/asound ]; return $?; fi
	[ -d "/proc/asound/card$1" ] || [ -d "/proc/asound/$1" ]
}

start_pre() {
	[ -x /usr/sbin/alsactl ] || return 1
	[ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
	. /usr/share/alsa/utils.sh
	executable amixer || { eerror "No amixer program available."; return 1; }
}

start()
{
	start_pre || return $?
	local EXITSTATUS=0 TARGET_CARD="${1:-all}"
	ebegin "Setting up ALSA"
	for path in $(ls -l "${_sysfs}/bus/pci/devices/" | grep -o 'devices/pci.*'); do
		_class=
		if [ -n "$path" ] && [ -f "${_sysfs}/${path}/class" ]; then
			_class=$(cat "${_sysfs}/${path}/class")
		fi
		case "$_class" in
			0x0401*|0x0403*)
				_modalias=$(cat "${_sysfs}/${path}/modalias")
				/sbin/modprobe -b -q -- "$_modalias"
				;;
		esac
	done
	# With vdev, cards may still be absent briefly; do not fail the boot.
	if ! card_OK "$TARGET_CARD"; then
		ewarn "no sound cards loaded yet"
		eend 0
		return 0
	fi
	preinit_levels "$TARGET_CARD" || EXITSTATUS=1
	if ! restore_levels "$TARGET_CARD"; then
		sanify_levels "$TARGET_CARD" || EXITSTATUS=1
		restore_levels "$TARGET_CARD" >/dev/null 2>&1 || :
	fi
	eend $EXITSTATUS
	return 0
}

stop()
{
	[ -x /usr/sbin/alsactl ] || return 0
	[ -d "$ALSACTLRUNTIME" ] || mkdir -p "$ALSACTLRUNTIME"
	. /usr/share/alsa/utils.sh 2>/dev/null || true

	local EXITSTATUS=0 TARGET_CARD="${1:-all}"
	ebegin "Shutting down ALSA"
	if ! card_OK "$TARGET_CARD"; then
		eend 0
		return 0
	fi
	store_levels "$TARGET_CARD" || EXITSTATUS=1
	eend $EXITSTATUS
	return 0
}

reload()
{
	stop || true
	start
}
