#!/bin/sh

# xpp_fxloader: load Xorcom Astribank (XPP) firmware
#
# This script can be run manually or from hotplug/udev.
#
# Firmware files should be located in $FIRMWARE_DIR which defaults:
# 	1. /usr/share/zaptel
#	2. Can be overidden by setting $FIRMWARE_DIR in the environment
#	3. Can be overidden by setting $FIRMWARE_DIR in /etc/default/zaptel
#
# Manual Run
# ##########
#
#   path/to/xpp_fxloader load
#
# Make sure the firmware files are in $FIRMWARE_DIR
#
# UDEV Installation
# #################
#
# Copy xpp.rules to /etc/udev/udev.d and xpp_fxloader to /etc/hotplug/usb/ .
#
# Hotplug Installation
# ####################
#
# Copy this file and the file xpp_fxloader.usermap to /etc/hotplug/usb/ .
#
# 
# Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
# Copyright (C) 2006, Xorcom
#
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

set -e

# Make sure fxload is in the path:
PATH="$PATH:/usr/local/sbin:/sbin:/usr/sbin"
export PATH

me=`basename $0`
DEBIAN_DEFAULTS="/etc/default/zaptel"
REDHAT_DEFAULTS="/etc/sysconfig/zaptel"

status_fd=3

if [ -r "$DEBIAN_DEFAULTS" -a -r "$REDHAT_DEFAULTS" ]; then
	echo 1>&2 "$0: Both '$DEBIAN_DEFAULTS' and '$REDHAT_DEFAULTS' exist"
	exit 1
elif [ -r "$DEBIAN_DEFAULTS" ]; then
	DEFAULTS="$DEBIAN_DEFAULTS"
elif [ -r "$REDHAT_DEFAULTS" ]; then
	DEFAULTS="$REDHAT_DEFAULTS"
fi

if [ -t 2 ]; then
	LOGGER="logger -i -t '$me' -s"
else
	LOGGER="logger -i -t '$me'"
fi

USBFS_PREFIX=/proc/bus/usb
DEVUSB_PREFIX=/dev/bus/usb
USB_PREFIX=

FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/zaptel}"

FIRM_FXS=$FIRMWARE_DIR/FPGA_FXS.hex

FPGA_LOAD=${FPGA_LOAD:-/usr/sbin/fpga_load}
USB_FW="${USB_FW:-USB_FW.hex}"

if [ -r "$DEFAULTS" ]; then
	. "$DEFAULTS"
fi

if [ "$USB_PREFIX" = '' ]; then
	if [ -d "$DEVUSB_PREFIX" ]; then
		USB_PREFIX=$DEVUSB_PREFIX
	elif [ -r "$USBFS_PREFIX/devices" ]; then
		USB_PREFIX=$USBFS_PREFIX
	fi
fi

# With Kernels older that 2.6.10 it seems to be possible
# to trigger a race condition by running fxload or fpga_load 
# immediately after the detection of the device.
KERNEL_HAS_USB_RACE=0
case "`uname -r`" in 2.6.[89]*) KERNEL_HAS_USB_RACE=1;; esac
sleep_if_race() {
  if [ "$KERNEL_HAS_USB_RACE" = '1' ]; then
    sleep 2
  fi
}

find_dev() {
  v_id=$1
  p_id=$2
  
  lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"$USB_PREFIX/%s/%s \",\$2,\$4}"
}

do_fxload() {
  sleep_if_race
  ( fxload -t fx2 $* 2>&1 1>/dev/null || exit 1 ) | $LOGGER
}

load_fw() {
  v_id=$1
  p_id=$2
  fw=$3
  
  devices=`find_dev $v_id $p_id`
  for dev in $devices
  do
    $LOGGER "USB Firmware $FIRMWARE_DIR/$fw into $dev"
    do_fxload -D $dev -I $FIRMWARE_DIR/$fw || exit 1
  done
}

load_fpga() {
  v_id=$1
  p_id=$2
  fw=$3
  
  devices=`find_dev $v_id $p_id`
  for dev in $devices
  do
	(
	card_ver=`$FPGA_LOAD -g -D $dev | sed -n 's/^.*Release: *//'`

	$LOGGER "FPGA Firmware into $dev"
	sleep_if_race
	(
		$FPGA_LOAD -D "$dev" -I "$FIRMWARE_DIR/$fw" -i
		echo $? >$status_fd
	)>| $LOGGER
	status=`cat <$status_fd`
	if [ "$status" != 0 ]; then
		echo "fpga_load failed with status $status" | $LOGGER
		exit 77
	fi
	) &
	sleep 0.4
  done
  wait
}

numdevs() {
  v_ids="$1"
  p_ids="$2"

  for v in $v_ids
  do
    (
      for p in $p_ids
      do
        find_dev $v $p
      done
    )
  done | wc -w
}

wait_renumeration() {
  num="$1"
  v_ids="$2"
  p_ids="$3"

  while
    n=`numdevs "$v_ids" "$p_ids"`
    [ "$num" -gt "$n" ]
  do
    echo -n "."
    sleep 1
  done
  echo "Got all $num devices"
}

reset_fpga() {
  totaldevs=`numdevs e4e4 '11[3456][012]'`
  devices=`find_dev e4e4 '11[3456][12]'`
  echo "Reseting devices [$totaldevs devices]"
  for dev in $devices
  do
	$LOGGER "Resetting FPGA Firmware on $dev"
	sleep_if_race
	$FPGA_LOAD -D "$dev" -r 2>&1 >/dev/null | $LOGGER
	status=$PIPESTATUS
	if [ "$status" != 0 ]; then
		echo "fpga_load failed removing with status $status" | $LOGGER
		exit 78
	fi
  done
  if [ "$1" = 'wait' ]; then
	  wait_renumeration $totaldevs e4e4 '11[3456]0'
  fi
}

#########################
##
## Manual run
##

# to run manually, pass the parameter 'xppdetect'
case "$1" in
udev) 
	# the following emulate hotplug's environment from udev's environment:
	DEVICE="$DEVNAME"
	PRODUCT="$2"
	# skip on to the rest of the script. Don't exit.
	;;
reset-wait)
	reset_fpga wait
	;;
reset)
	reset_fpga
	;;
xppdetect|load|usb)
	numdevs=`numdevs e4e4 '11[3456][01]'`
	echo "--------- FIRMWARE LOADING: ($1) [$numdevs devices]"

	load_fw e4e4 1130 $USB_FW
	load_fw e4e4 1140 $USB_FW
	load_fw e4e4 1150 $USB_FW
	load_fw e4e4 1160 $USB_FW
	wait_renumeration $numdevs e4e4 '11[3456]1'
	if [ "$1" != 'usb' ]
	then
		load_fpga e4e4 1131 FPGA_FXS.hex
		load_fpga e4e4 1141 FPGA_1141.hex
		load_fpga e4e4 1151 FPGA_1151.hex
		load_fpga e4e4 1161 FPGA_1161.hex
		wait_renumeration $numdevs e4e4 '11[3456]2'
	fi

	sleep 3		# Let it stabilize
	echo "--------- FIRMWARE IS LOADED"
	exit 0
	;;
help)
	echo "$0: Astribank firmware loading script."
	echo "Usage: "
	echo "$0 load  : manual firmware loading."
	echo "$0 usb   : manual firmware loading: USB firmware only."
	echo "$0 help  : this text."
	echo ""
	echo "('xppdetect' is an alias of 'load')"
	exit 0
	;;
esac

#########################
##
## Hotplug run
##

# allow disabling automatic hotplugging:
if [ "$XPP_HOTPLUG_DISABLED" != '' ]; then
	$LOGGER -p kern.info "Exiting... XPP_HOTPLUG_DISABLED"
	exit 0
fi

if [ "$ACTION" = "add" ] && [ -w "$DEVICE" ]
then
	$LOGGER "Trying to find what to do for product $PRODUCT, device $DEVICE"
	prod_id=`echo "$PRODUCT" | cut -d/ -f2`
	case "$PRODUCT" in
	e4e4/11[345]0/*)
		FIRM_USB="$FIRMWARE_DIR/$USB_FW"
		$LOGGER "Loading firmware '$FIRM_USB' into '$DEVICE'"
		do_fxload -D "$DEVICE" -I "$FIRM_USB"
		;;
	e4e4/11[345]1/*)
		if [ "$prod_id" = 1131 ]; then
			FIRM_FPGA="$FIRMWARE_DIR/FPGA_FXS.hex"	# Legacy
		else
			FIRM_FPGA="$FIRMWARE_DIR/FPGA_$prod_id.hex"
		fi
		sleep_if_race
		$FPGA_LOAD -D "$DEVICE" -I "$FIRM_FPGA" 2>&1 >/dev/null | $LOGGER
		;;
	esac	
fi
