#!/bin/bash

### BEGIN INIT INFO
# Provides: edtinit
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Load EDT driver modules
# Description: 
#  Run at boot time (as determined by installation scripts) to find Engineering Design
#  Team Inc. (EDT) hardware on the given system and load the appropriate system drivers
#  (creating soft links as neccessary) for the hardware to run properly on the system.
#  If system kernel version has changed, the driver will automatically be recompiled.
#
### END INIT INFO
#
# Copyright (c) 2018 Engineering Design Team (EDT), Inc.
# All rights reserved. This file is subject to the terms and conditions
# of the EULA defined at www.edt.com/terms-of-use
#
# Technical Contact:<tech@edt.com>
#
# usage: ./edtinit <start|stop|restart>
#

module="edt"
device="edt"
mode="666"
grep -qw "^wheel" /etc/group
[ $? -ne 0 ] || group="wheel"

lspci=`which lspci 2>/dev/null`

if [ "$lspci" == "" ] ; then
	if [ -x /usr/bin/lspci ]; then
		lspci=/usr/bin/lspci
	elif [ -x /usr/sbin/lspci ]; then 
		lspci=/usr/sbin/lspci
	elif [ -x /bin/lspci ]; then
		lspci=/bin/lspci
	elif [ -x /sbin/lspci ]; then
		lspci=/sbin/lspci
	else 
		echo "FATAL ERROR:It looks like you don't have 'lspci'!"
		echo "Make sure the that pciutils package is installed on your system."
		exit 1
	fi
fi

lsmod=`which lsmod 2>/dev/null`

if [ "$lsmod" == "" ] ; then
	if [ -x /usr/bin/lsmod ]; then
		lsmod=/usr/bin/lsmod
	elif [ -x /usr/sbin/lsmod ]; then 
		lsmod=/usr/sbin/lsmod
	elif [ -x /bin/lsmod ]; then
		lsmod=/bin/lsmod
	elif [ -x /sbin/lsmod ]; then
		lsmod=/sbin/lsmod
	else 
		echo "FATAL ERROR:It looks like you don't have 'lsmod'!"
		echo "Make sure that the kmod package is installed on your system."
		exit 1
	fi
fi

function check_run_pdvload()
{
	RUN_PDVLOAD="n"
	
	if [ -f /opt/EDTpdv/.run_pdvload ] ; then
		RUN_PDVLOAD=`cat /opt/EDTpdv/.run_pdvload`

		if [ $RUN_PDVLOAD == "y" ] ; then 
			cd /opt/EDTpdv 
			./pdvload
		fi
	fi
}

function check_run_pcdload()
{
	RUN_PCDLOAD="n"
	
	if [ -f /opt/EDTpcd/.run_pcdload ] ; then 
		RUN_PCDLOAD=`cat /opt/EDTpcd/.run_pcdload`

		if [ $RUN_PCDLOAD == "y" ] ; then 
			cd /opt/EDTpcd 
			./pcdload
		fi
	fi
}

function load_edt_module()
{
	# call modprobe
	echo "/sbin/modprobe edt"
	/sbin/modprobe edt

	major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"`
	echo major is $major
}

#ID names to values map
declare -a edt_board_id_set
edt_board_id_set=(
	#Board ID    Board Name       Board Description        Node	# Channels
	#---------   -------------    -------------------      ------	----------				 
	"0x0000      P11W:	      P11W:		       p11w:	1"
	"0x0001      P16D:	      P16D:		       p16d:	1"
	"0x0010      PDV:	      PDV:		       pdv:	1"
	"0x0048      PDVA:	      PDV:		       pdv:	1"
	"0x004c      PDVFOX:	      PDV-FOX:		       pdv:	4"
	"0x0092      PE4DVAFOX:       PEDV-FOX:		       pdv:	4"
	"0x004c      PE8DVFOX:	      PEDV-FOX:		       pdv:	4"
	"0x009e      PE4DVVLFOX:      VisionLink-FOX:		pdv:	 4"
	"0x0038      PDVFCI_AIAG:     PDV:		       pdv:	1"
	"0x0039      PDVFCI_USPS:     DV_FCI:		       pdv:	2"
	"0x0014      PDVK:	      PDV:		       pdv:	1"
	"0x0030      PDVFOI:	      PDV:		       pdv:	1"
	"0x0015      PDV44:	      PDV:		       pdv:	1"
	"0x0034      PDVCL:	      PDVCL:		       pdv:	3"
	"0x009d      PE1DVVL:	      VisionLink-F1:		pdv:	 3"
	"0x00a3      PE4DVVL:	      VisionLink-F4:		pdv:	 3"
	"0x0091      PE4DVCL:	      PEDV-CL:		       pdv:	3"
	"0x0093      PE8DVCL:	      PEDV-CL:		       pdv:	3"
	"0x009f      PC104ICB:	      PC104-ICB:	       pdv:	3"
	"0x0016      PDVAERO:	      PDV:		       pdv:	1"
	"0x0024      PGP_RGB:	      PCD:		       pcicd:	1"
	"0x0044      PCDA:	      PCD:		       pcicd:	4"
	"0x0040      PSS4:	      PCD:		       pcicd:	4"
	"0x0050      PGS4:	      PCD:		       pcicd:	4"
	"0x0011      PCD20:	      PCD:		       pcicd:	4"
	"0x0012      PCD40:	      PCD:		       pcicd:	4"
	"0x0013      PCD60:	      PCD:		       pcicd:	4"
	"0x0021      PGP20:	      PCD:		       pcicd:	4"
	"0x0022      PGP40:	      PCD:		       pcicd:	4"
	"0x0023      PGP60:	      PCD:		       pcicd:	4"
	"0x0026      PGP_ECL:	      PCD:		       pcicd:	4"
	"0x0027      PCD_16:	      PCD-16:		       pcicd:  16"
	"0x0041      PSS16:	      PCD-16:		       pcicd:  16"
	"0x0051      PGS16:	      PCD-16:		       pcicd:  16"
	"0x0045      PCDA16:	      PCD-16:		       pcicd:  16"
	"0x004b      PE4CDA16:	      PCD-16:		       pcicd:  16"
	"0x0047      PE4CDA:	      PCD:		       pcicd:	4"
	"0x004e      PCDFOX:	      PCD:		       pcicd:	4"
	"0x0046      PCDCL:	      PCD:		       pcicd:	4"
	"0x004a      PDVCL2:	      PDV-CLS (PDV-CLS2):      pdv:	4"
	"0x0094      PE4DVFCI:	      <>:		       pdv:	2"
	"0x0095      PE8DVFCI:	      <>:		       pdv:	2"
	"0x0096      PE8LX1:	      PE8 LX1:		       pcicd:	1"
	"0x0097      PE8LX16:	      PE8 LX16:		       pcicd:  16"
	"0x0098      PE8LX16_LS:      PE8 LX16:		       pcicd:  16"
	"0x009c      PE8LX32:	      PE8 LX32:		       pcicd:  32"
	"0x009a      PE4AMC16:	      PE8 LX16:		       pcicd:  16"
	"0x009b      PE8DVCLS:	      PE8DV-CLS:	       pdv:	2"
	"0x00a9      PE8VLCLS:	      VisionLink CLS:	       pdv:	2"
	"0x00a0      PE8G3S5:	      PE8 G3S5:		       pcicd:  16"
	"0x00ab      PE8G3A5:	      PE8 G3A5:		       pcicd:  16"
	"0x00a1      WSU1:	      WSU1/SNAP1:	       pcicd:  16"
	"0x00a2      SNAP1:	      WSU1/SNAP1:	       pcicd:  16"
	"0x00a4      PE4BL_RADIO:     LCR RADIO BLADE:	       pcicd:  16"
	"0x00ad      PE4BL_RXLFRADIO: LCR RX/LF RADIO BLADE:   pcicd:  16"
	"0x00af      PE4BL_TXLFRADIO: LCR TX/LF RADIO BLADE:   pcicd:  16"
	"0x00a5      PE1BL_TIMING:    LCR TIMING BLADE:        pcicd:	1"
	"0x00a6      PE8BL_NIC:       LCR NIC BLADE:	       pcicd:  16"
	"0x00a7      LCRBOOT:	      LCR BOOT:		       pcicd:	1"
	"0x00a8      PE8G2V7:	      PE8 G2V7:		       pcicd:  32"
	"0x00aa      PE8G3KU:	      PE8 G3KU:		       pcicd:  16"
	"0x00ae      RESERVED_AE:     <RESERVED_AE>:	       <>:	1" # was 2X12.5G/E3		
	"0x00b0      PE8G2CML:	      PE8 G2CML:	       pcicd:  16"
	"0x00b2      PE8BL_WBDSP:     PE8 WB DSP BLADE:        pcicd:  16"
	"0x00b1      PE1BL_WBADC:     PE1 WB ADC BLADE:        pcicd:	1"
	"0x00b3      PE16G3_OCTEON3:  PE16 OCTEON:	       pcicd:	4"
	"0x00b4      PE8BL_40G:       PE8 40G BLADE:	       pcicd:  16"
	"0x00b5      PE8G2_HH_RADIO:  HH RADIO BLADE:	       pcicd:  16"
	"0x00b6      PE8G2_HH_RADIO_M2:  HH RADIO BLADE M2 carrier:	pcicd:	16"
# 
# IMPORTANT:When adding IDs to this table, always do so in the same place in the list in edt_find_boards() 
#  (which is located in pci/shared/edt_lnx_kernel.c)."
#
) # end edt_board_id_set table
	
function build_boardids()
{
	ids=`$lspci -d 123d:* -n | awk 'BEGIN {FS=":"} ; {print "0x" $NF}' | awk '{print $1}'`
	declare -i board # board is an integer

	echo "ids = $ids"

	board_ids=()

	##
	## The following sorts the boards according to the order of the
	## device structs in the driver so that minor numbers match.
	##
	for entry in "${edt_board_id_set[@]}"; do
		entry_id=`echo $entry | tr -s ' ' | cut -d ' ' -f1`
		for board in $ids ; do
			if [ $((entry_id)) -eq $board ] ; then
				board_ids+=($board)
			fi
		done
	done

	ids="${board_ids[*]}"
	echo "sorted ids = $ids"
}

function build_links()
{
	echo "Build Links..."
	# keep independent indices for individual boards

	index=0
	p11windex=-1
	p16dindex=-1
	pcdindex=-1
	pdvindex=-1
	devindex=0

	rm -f /dev/edt* /dev/pdv* /dev/pcicd* /dev/p16d* /dev/p11w*

	declare -i board # board is an integer

	for board in $ids ; do
		echo BOARD $board
		echo "devindex = $devindex"
		echo "Setting up board $board ($module$index)"
		mknod /dev/${device}${index} c $major $devindex
		echo "mknod /dev/${device}${index} c $major $devindex"
		if grep "^wheel:" /etc/group; then
			chgrp $group /dev/${device}[0-9]*
		fi
		chmod $mode  /dev/${device}[0-9]*
	
		board_line=""
		for entry in "${edt_board_id_set[@]}"; do
			entry_id=`echo $entry | tr -s ' ' | cut -d ' ' -f1`
			if [ $((entry_id)) -eq $board ] ; then
				board_line=$entry
			fi
		done

		echo Type `echo $board_line | tr -s ' ' | cut -d ':' -f2`  
		edt_devname=`echo $board_line | tr -s ' ' | cut -d ':' -f3 | sed 's/ //'`
		channel_ct=`echo $board_line | tr -s ' ' | cut -d ':' -f4 | sed 's/ //'`
		p_index=0
	
		case `echo $edt_devname` in
		p11w)
			((p11windex++))
			p_index=$p11windex
			;;
		p16d)
			((p16dindex++))
			p_index=$p16dindex
			;;
		pcicd)
			((pcdindex++))
			p_index=$pcdindex
			;;
		pdv)
			((pdvindex++))
			p_index=$pdvindex
			;;
		*)
			echo "Unknown EDT Board"
			continue
			;;
		esac
		
		ln -s /dev/${device}${index} /dev/${edt_devname}${p_index}
		
		minor=0
		while [ ${minor} -lt $channel_ct ]; do
			# echo "$board_hex: mknod /dev/${device}${index}_${minor} c $major $devindex"
			mknod /dev/${device}${index}_${minor} c $major $devindex
			ln -s /dev/${device}${index}_${minor} /dev/${edt_devname}${p_index}_${minor}
			if grep "^wheel:" /etc/group; then
				chgrp $group /dev/${device}${index}_${minor}
			fi 
			chmod $mode /dev/${device}${index}_${minor}
			((minor++))
			((devindex++))
		done
		
		ls -l /dev/${device}${index}*
		
		((index++))
	done

	ln -s /dev/${device}0 /dev/${device}
}

function check_kernel_version()
{
	edt_pkg=$1
	if [[ "$edt_pkg" != "pcd" && "$edt_pkg" != "pdv" ]]; then
		echo "Parameter must be \'pcd\' or \'pdv\'."
	fi

	# Enter EDT package directory.
	if [[ ! -e /opt/EDT$edt_pkg ]]; then
		echo "/opt/EDT$edt_pkg not found, skipping."
		return
	fi
	cd /opt/EDT$edt_pkg

	# File containing the kernel version on which the EDT driver was last compiled.
	compilation_log="EDT_driver_compilation.log"
        echo "=== === === === === === === === ===" | tee -a ${compilation_log}
        date | tee -a ${compilation_log}

	# Check the edt modules kernel version, if differing from current, rebuild.
	modinfo edt -F vermagic | grep `uname -r`
	if [ $? -ne 0 ]; then
		# Current kernel version does not match last_compiled_kernel.
		last=`modinfo edt -F vermagic | cut -d " " -f1`
		echo "--- System kernel version has changed; driver previously compiled for \"$last\"." | tee -a ${compilation_log}
		echo "--- Building driver for kernel \"$(uname -r)\" now ..." | tee -a ${compilation_log}
		make driver 2>&1 | tee -a ${compilation_log}
		if [[ "$?" -ne 0 ]]; then
			echo "--- Build failed." | tee -a ${compilation_log}
			exit 1
		else
			echo "--- Build complete." | tee -a ${compilation_log}
		fi
	else
		echo "--- Clean installation for kernel \"$(uname -r)\"." | tee -a ${compilation_log}
	fi
        echo "" | tee -a ${compilation_log}
}

function edtload()
{
	echo "function edtload:"

	check_kernel_version "pcd"
	check_kernel_version "pdv"

	build_boardids

	if [ "$ids" == "" ] ; then
		echo "No EDT boards found; exiting edtinit"
		return
	fi

	load_edt_module

	build_links

	check_run_pdvload
	check_run_pcdload

	grep "dev.pcd.reserved_pages" /etc/sysctl.conf > /tmp/pcd.reserve

	if [ $? -eq 0 ]; then
		pages=`cut -d= -f 2 /tmp/pcd.reserve | sed 's: ::g'`
		echo "Reserving $pages pages"
		sysctl -w dev.pcd.reserved_pages=$pages
	fi
}

function edt_unload()
{
	echo "function edt_unload:Unloading module $module"

	if [ "`$lsmod | grep \"^$module\b\"`" == "" ] ; then
		echo " Error: module is not loaded."
		exit 1
	fi

	if [ `lsof -t /dev/edt* | wc -l` != 0 ] ; then
		echo " Error: cannot unload module while DMA is active."
		exit 1
	fi

	/sbin/rmmod $module

	# Remove stale nodes

	rm -f /dev/${device}[0-3] /dev/${device}
	rm -f /dev/${device}priv
	rm -f /dev/${device}pipe[0-3] /dev/${device}pipe
	rm -f /dev/${device}single
	rm -f /dev/${device}uid
	rm -f /dev/${device}wuid

	rm -f /dev/pcicd*
	rm -f /dev/pdv*
	rm -f /dev/p16d*
	rm -f /dev/p11w*
	rm -f /dev/edt*
}


case $1 in
	start)
		$lsmod | grep "\<edt\>"  > /dev/null
		
		if [ $? = 0 ] ; then
			echo "Edt module already loaded - removing"
			edt_unload
		fi
		
		edtload
		;;
	stop) 
		edt_unload
		;;
	restart)
		edt_unload
		edtload
		;;
esac

exit 0
