#!/bin/bash

prefix=/usr
exec_prefix=/usr

VISDN_DIR=/etc/visdn

VISDNCTL=/usr/sbin/visdnctl
#VISDNCTL="echo visdnctl"

modprobe visdn-netdev
modprobe visdn-streamport
modprobe visdn-ppp
modprobe visdn-timer-system
modprobe visdn-ec
modprobe visdn-hfc-4s
modprobe visdn-hfc-pci
modprobe visdn-hfc-usb
modprobe visdn-hfc-e1

NETDEV_CONTROL_DEVICE="/dev/visdn/netdev-control"
ROUTER_CONTROL_DEVICE="/dev/visdn/router-control"

function check_device()
{
	if [ ! -c "$FILENAME" ]; then
		echo "File $FILENAME is not a character device";
		exit 1
	fi
}

function wait_for_device()
{
	local FILENAME=$1

	if [ ! -e "$FILENAME" ]; then
		echo -n "Waiting for device file $FILENAME to appear..."

		for I in 1 2 3 4 5 6 7 8 9 10; do
			sleep 2

			echo -n "."

			if [ -e "$FILENAME" ]; then
				check_device "$FILENAME"

				echo "done"
				return
			fi
		done

		echo "timeout (ensure udev is configured correctly)"
		exit 1
	fi

	check_device "$FILENAME"
}

function set_attribute()
{
	local NAME=$1
	local VALUE=$2

	if [ ! -f "$NAME" ]; then
		echo "Waiting for attribute $NAME..."

		sleep 3

		if [ ! -f "$NAME" ]; then
			echo "Attribute $NAME does not exist"
			return
		fi
	fi

	echo "$VALUE" > "$NAME"
}

function read_netdev_block
{
	local NETDEV_REQ_DEVNAME=$1
	local NETDEV_PROTOCOL="lapd"
	local NETDEV_TYPE="bri"
	local NETDEV_MODE="p2mp"

	local LINE
	while read -u 9 LINE; do
		if [[ $LINE =~ '</(.*)>' ]]; then
			if [ "${BASH_REMATCH[1]}" == 'netdev' ]; then
				$VISDNCTL netdev create \
					"$NETDEV_REQ_DEVNAME" \
					"$NETDEV_PROTOCOL" || return

				$VISDNCTL netdev \
					set $NETDEV_DEVNAME type $NETDEV_TYPE

				$VISDNCTL netdev \
					set $NETDEV_DEVNAME role $NETDEV_ROLE

				$VISDNCTL netdev \
					set $NETDEV_DEVNAME mode $NETDEV_MODE

				return
			else
				echo "Invalid block closing tag '$BASH_REMATCH'"
				exit 1
			fi
		elif [[ $LINE =~ '[	 ]*([a-zA-Z]+)[	 ]+(.*)' ]]; then
			case "${BASH_REMATCH[1]}" in
			protocol) NETDEV_PROTOCOL=${BASH_REMATCH[2]} ;;
			type) NETDEV_TYPE=${BASH_REMATCH[2]} ;;
			role) NETDEV_ROLE=${BASH_REMATCH[2]} ;;
			mode) NETDEV_MODE=${BASH_REMATCH[2]} ;;
			*)
				echo "Unknown directive ${BASH_REMATCH[1]}"
				continue
			;;
			esac
		fi
	done

	echo "Unexpected EOF in netdev block"
}

function handle_netdev_connectmodule
{
	local CHANNEL=$1
	local PARAMETERS=$2

	if [[ ! "$PARAMETERS" =~ '^([-_0-9a-z.]+)[ 	]*(.*)$' ]]; then
		echo "Invalid parameters for directive connectmodule"
		exit 1
	fi

	if [ "${BASH_REMATCH[2]}" = "echo" ]; then
		$VISDNCTL connect \
			"$CHANNEL" \
			"/sys/class/net/${BASH_REMATCH[1]}/visdn_channel_e" ||
				return
	else
		$VISDNCTL connect \
			"$CHANNEL" \
			"/sys/class/net/${BASH_REMATCH[1]}/visdn_channel" ||
				return
	fi

	$VISDNCTL netdev set "${BASH_REMATCH[1]}" up
}

function handle_connectmodule
{
	local CHANNEL=$1
	local MODULE=$2
	local PARAMETERS=$3

	case "$MODULE" in
	netdev) handle_netdev_connectmodule "$CHANNEL" "$PARAMETERS" ;;
	*)
		echo "Unsupported module $MODULE"
		exit 1
	;;
	esac
}

function read_chan_block
{
	local CHAN_NAME=$1

	if [ ! -d "$SYSFS_CARD_OBJ/$PORT_NAME" ]; then
		echo "Port $PORT_NAME not found"
		exit 1
	fi

	if [ ! -d "$SYSFS_CARD_OBJ/$PORT_NAME/$CHAN_NAME" ]; then
		echo "Channel $PORT_NAME/$CHAN_NAME not found"
		exit 1
	fi

	CHANNEL=`basename \`readlink $SYSFS_CARD_OBJ/$PORT_NAME/$CHAN_NAME\``

	local LINE
	while read -u 9 LINE; do
		if [[ "$LINE" =~ '^[ 	]*#' ]]; then
			continue
		elif [[ $LINE =~ '</(.*)>' ]]; then
			if [ "${BASH_REMATCH[1]}" == 'chan' ]; then

				return
			else
				echo "Invalid block closing tag '$BASH_REMATCH'"
				exit 1
			fi

		elif [[ $LINE =~ '[	 ]*([a-zA-Z]+)[	 ]+(.*)' ]]; then
			case "${BASH_REMATCH[1]}" in
			connectmodule)
				if [[ ! ${BASH_REMATCH[2]} =~ '^([-a-z0-9_]+)[	 ]+(.*)$' ]]; then
					echo "Invalid connectmodule directive parameters"
					exit 1
				fi

				handle_connectmodule \
					"$CHANNEL" \
					"${BASH_REMATCH[1]}" \
					"${BASH_REMATCH[2]}"
			;;
			*)
				echo "Unknown directive ${BASH_REMATCH[1]}"
				continue
			;;
			esac
		fi
	done

	echo "Unexpected EOF in netdev block"
}

function read_port_block
{
	local PORT_NAME=$1

	if [ ! -d "$SYSFS_CARD_OBJ/$PORT_NAME" ]; then
		echo "Port object $PORT_NAME not found"
		exit 1
	fi

	if [ ! -f "$SYSFS_CARD_OBJ/$PORT_NAME/enabled" ]; then
		echo "Attribute 'enabled' not found"
		exit 1
	fi

	local LINE
	while read -u 9 LINE; do
		if [[ "$LINE" =~ '^[ 	]*#' ]]; then
			continue
		elif [[ $LINE =~ '</(.*)>' ]]; then
			if [ "${BASH_REMATCH[1]}" == 'port' ]; then

				echo "1" > "$SYSFS_CARD_OBJ/$PORT_NAME/enabled"

				return
			else
				echo "Invalid block closing tag '$BASH_REMATCH'"
				exit 1
			fi
		elif [[ "$LINE" =~ '<chan (.*)>' ]]; then

			CHAN_NAME=${BASH_REMATCH[1]}

			read_chan_block "$CHAN_NAME"

		elif [[ "$LINE" =~ '[ 	]*attribute[ 	]+(.*)' ]]; then
			if [[ ! "${BASH_REMATCH[1]}" =~ '^([-a-z0-9_]+)[ 	]+(.*)$' ]]; then
				echo "Invalid attribute directive format"
				continue
			fi

			set_attribute	\
				"$SYSFS_CARD_OBJ/$PORT_NAME/${BASH_REMATCH[1]}" \
				"${BASH_REMATCH[2]}"

		elif [[ $LINE =~ '[	 ]*([a-zA-Z]+)[	 ]+(.*)' ]]; then
			case "${BASH_REMATCH[1]}" in
			xxx) XXX=${BASH_REMATCH[2]} ;;
			*)
				echo "Unknown directive ${BASH_REMATCH[1]}"
				continue
			;;
			esac
		fi
	done

	echo "Unexpected EOF in netdev block"
}

wait_for_device $NETDEV_CONTROL_DEVICE
wait_for_device $ROUTER_CONTROL_DEVICE

for CARD in `ls $VISDN_DIR`; do
	if [[ ! "$CARD" =~ '^device-([a-z0-9]+)-(.*)$' ]]; then
		continue
	fi

	CONFIGFILE="$VISDN_DIR/$CARD"

	BUS=${BASH_REMATCH[1]}
	BUS_ID=${BASH_REMATCH[2]}
	SYSFS_CARD_OBJ="/sys/bus/$BUS/devices/$BUS_ID"

	if [ ! -d "$SYSFS_CARD_OBJ" ]; then
		echo "Card $BUS $BUS_ID not found, configuration ignored"
		continue
	fi

	exec 9<$CONFIGFILE

	while read -u 9 LINE; do

		if [[ "$LINE" =~ '^[ 	]*#' ]]; then
			continue
		elif [[ "$LINE" =~ '<netdev (.*)>' ]]; then

			NETDEV_DEVNAME=${BASH_REMATCH[1]}

			read_netdev_block "$NETDEV_DEVNAME"
		elif [[ "$LINE" =~ '<port (.*)>' ]]; then

			PORT_NAME=${BASH_REMATCH[1]}

			read_port_block "$PORT_NAME"

		elif [[ "$LINE" =~ '[ 	]*attribute[ 	]+(.*)' ]]; then
			if [[ ! "${BASH_REMATCH[1]}" =~ '^([-a-z0-9_]+)[ 	]+(.*)$' ]]; then
				echo "Invalid attribute directive format"
				continue
			fi

			set_attribute	\
				"$SYSFS_CARD_OBJ/${BASH_REMATCH[1]}" \
				"${BASH_REMATCH[2]}"
		fi

	done

	exec 9<&-

	declare -i i
done
