#!/bin/sh

# This script handles all aspects of installing and configuring the
# HSF drivers under Linux.  It aspires to be distribution-neutral
#
# Written by Marc Boucher <marc@mbsi.ca> for Conexant Systems Inc.

#
# Copyright (c) 2001 Conexant Systems, Inc.
# 
# 1.   Permitted use. Redistribution and use in source and binary forms,
# with or without modification, are permitted under the terms set forth
# herein.
# 
# 2.   Disclaimer of Warranties. CONEXANT AND OTHER CONTRIBUTORS MAKE NO
# REPRESENTATION ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.
# IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTIES OF ANY KIND.
# CONEXANT AND OTHER CONTRIBUTORS DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, GOOD TITLE AND AGAINST INFRINGEMENT.
# 
# This software has not been formally tested, and there is no guarantee that
# it is free of errors including, but not limited to, bugs, defects,
# interrupted operation, or unexpected results. Any use of this software is
# at user's own risk.
# 
# 3.   No Liability.
# 
# (a) Conexant or contributors shall not be responsible for any loss or
# damage to Company, its customers, or any third parties for any reason
# whatsoever, and CONEXANT OR CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY
# ACTUAL, DIRECT, INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL, OR CONSEQUENTIAL
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED, WHETHER IN CONTRACT, STRICT OR OTHER LEGAL THEORY OF
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.
# 
# (b) User agrees to hold Conexant and contributors harmless from any
# liability, loss, cost, damage or expense, including attorney's fees,
# as a result of any claims which may be made by any person, including
# but not limited to User, its agents and employees, its customers, or
# any third parties that arise out of or result from the manufacture,
# delivery, actual or alleged ownership, performance, use, operation
# or possession of the software furnished hereunder, whether such claims
# are based on negligence, breach of contract, absolute liability or any
# other legal theory.
# 
# 4.   Notices. User hereby agrees not to remove, alter or destroy any
# copyright, trademark, credits, other proprietary notices or confidential
# legends placed upon, contained within or associated with the Software,
# and shall include all such unaltered copyright, trademark, credits,
# other proprietary notices or confidential legends on or in every copy of
# the Software.
#

load_config()
{
	hsfconf_countryName="AUTO"
	hsfconf_countryT35c=""
	hsfconf_vendorId=""
	hsfconf_deviceId=""
	hsfconf_infFile=""
	hsfconf_firmFile=""
	if [ -f ${hsfetcdir}/config ]; then
		. ${hsfetcdir}/config
	else
		return 0
	fi
}

save_config()
{
	rm -f ${hsfetcdir}/config
	(
		echo "# WARNING: DO NOT EDIT THIS AUTOMATICALLY GENERATED FILE DIRECTLY !"
		echo "# TO PROPERLY CHANGE THE HSF MODEM CONFIGURATION, RUN \"hsfconfig\""
		echo "#"
		echo "hsfconf_countryName=\"${hsfconf_countryName}\""
		echo "hsfconf_countryT35c=\"${hsfconf_countryT35c}\""
		echo "hsfconf_vendorId=\"${hsfconf_vendorId}\""
		echo "hsfconf_deviceId=\"${hsfconf_deviceId}\""
		echo "hsfconf_infFile=\"${hsfconf_infFile}\""
		echo "hsfconf_firmFile=\"${hsfconf_firmFile}\""
	) > ${hsfetcdir}/config
}

show_config()
{
	echo ""
	echo "Current HSF configuration:"
	echo ""
	if [ -n "${hsfconf_countryT35c}" ]; then
		echo "	Country name: \"${hsfconf_countryName}\", T.35 code: `echo \"${hsfconf_countryT35c}\" | awk -F, '{print $2$1}'`"
	else
		echo "	Country name: \"${hsfconf_countryName}\""
	fi
	echo "	PCI Modem vendor-id: ${hsfconf_vendorId}, device-id: ${hsfconf_deviceId}"
	echo "	Driver inf filename: ${hsfconf_infFile}"
	if [ -n "${hsfconf_firmFile}" ]; then
		echo "	Board firmware file: ${hsfconf_firmFile}"
	fi
}

ask_yesno()
{
	$automode && return $2

	while true; do
		echo -n "$1"
		read answer
		case "${answer}" in
		[yY] | [yY][eE][sS])
			return 0
			;;
		[nN] | [nN][oO])
			return 1
			;;
		"")
			return $2
			;;
		*)
			echo "Enter 'yes' or 'no'"
			;;
		esac
	done
}

find_inf()
{
	_vendor=$1
	_device=$2

	for f in ${hsfinfdir}/*.inf; do

		if [ ! -e "${f}" ]; then
			continue
		fi

		if grep -qi "^PCI\\\\VEN_${_vendor}&DEV_${_device}&" "${f}"; then
			found_inf="${f}"
			return 0
		fi
	done

	return 1
}

auto_find_inf()
{
	lspci -m -n | \
		awk '-F"' '{print toupper($4)" "toupper($6)}' >/tmp/hsfconfig.pci.$$

	while read vendorid deviceid; do
		if find_inf "${vendorid}" "${deviceid}"; then
			rm -f /tmp/hsfconfig.pci.$$
			return 0
		fi
	done < /tmp/hsfconfig.pci.$$

	rm -f /tmp/hsfconfig.pci.$$
	return 1
}

lspci_text()
{
	if [ -r /proc/pci ]; then
		# First, try to get information directly from /proc/pci, as the kernel
		# usually has more recent pci.ids than the vendor's pciutils
		awk  '
			/Bus.*device.*function.*:/ {
				split($0,pcidev,/[ 	,:]*/);
				printf "%02x:%02x.%x ", pcidev[3],pcidev[5],pcidev[7];
				getline;
				gsub(/^[ 	]*/, "");
				print
			}' </proc/pci
	else
		lspci
	fi
}

choose_pcicomctrl()
{
	if ! ask_yesno "Is the modem presently installed in your system? [yes] " 0; then
		return 1
	fi

	lspci_text | egrep ' (Communication controller|Class ffff):' > /tmp/hsfconfig.com.$$
	if [ ! -s /tmp/hsfconfig.com.$$ ]; then
		echo ""
		echo "Sorry, no communication controllers were found."
		rm -f /tmp/hsfconfig.com.$$
		return 1
	fi

	while true; do
		echo ""
		awk '{
			sub(/^[0-9a-fA-F:\.][0-9a-fA-F:\.]*.Communication controller: /, "");
			sub(/^[0-9a-fA-F:\.][0-9a-fA-F:\.]*.Class ffff: /, "");
			print NR") "$0;
		}' < /tmp/hsfconfig.com.$$
		notfound="`awk 'END {print NR+1}' < /tmp/hsfconfig.com.$$`"
		echo "${notfound}) None of the above"

		echo ""
		echo -n "Which PCI communication controller is your HSF modem? (1-${notfound}) "
		read answer
		case "${answer}" in
		[0-9] | [0-9][0-9] )
			;;
		*)
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
			;;
		esac

		if ! [ "${answer}" -ge 1 -a "${answer}" -le "${notfound}" ]; then
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
		fi

		if [ "${answer}" -eq "${notfound}" ]; then
			rm -f /tmp/hsfconfig.com.$$
			return 1
		fi

		pcislot="`sed -n \"${answer} {
			s/ .*//;p
		}\" < /tmp/hsfconfig.com.$$`"

		lspci -m -n -s "${pcislot}" | \
			awk '-F"' '{print toupper($4)" "toupper($6)}' >/tmp/hsfconfig.com.$$

		vendorid=""
		deviceid=""
		read vendorid deviceid </tmp/hsfconfig.com.$$

		rm -f /tmp/hsfconfig.com.$$

		if [ -z "${vendorid}" -o -z "${deviceid}" ]; then
			return 1
		else
			return 0
		fi
	done
}

choose_inf()
{
	ls ${hsfinfdir}/*.inf > /tmp/hsfconfig.infs.$$

	if [ ! -s /tmp/hsfconfig.infs.$$ ]; then
		echo ""
		echo "Sorry, no INF files were found under ${hsfinfdir}."
		rm -f /tmp/hsfconfig.infs.$$
		return 1
	fi

	while true; do
		echo ""
		awk '{print NR") "$0}' < /tmp/hsfconfig.infs.$$
		notfound="`awk 'END {print NR+1}' < /tmp/hsfconfig.infs.$$`"
		echo "${notfound}) None of the above"

		echo ""
		echo -n "Which INF file would you like to use with your HSF modem? (1-${notfound}) "
		read answer

		case "${answer}" in
		[0-9] | [0-9][0-9] )
			;;
		*)
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
			;;
		esac

		if ! [ "${answer}" -ge 1 -a "${answer}" -le "${notfound}" ]; then
			echo "ERROR: Numeric answer between 1 and ${notfound} expected."
			continue
		fi

		if [ "${answer}" -eq "${notfound}" ]; then
			rm -f /tmp/hsfconfig.infs.$$
			return 1
		fi

		found_inf="`sed -n \"${answer} {
			s/ .*//;p
		}\" < /tmp/hsfconfig.infs.$$`"

		rm -f /tmp/hsfconfig.infs.$$

		if [ -z "${found_inf}" ]; then
			return 1
		else
			return 0
		fi
	done
}

ask_pci_id()
{
	while true; do
		echo ""
		echo -n "$1"
		read answer
		case "${answer}" in
		[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] )
			;;
		*)
			echo "ERROR: 4-digit hexadecimal number expected."
			continue
			;;
		esac

		pci_id="${answer}"
		return 0
	done
}

configure_inf()
{
	found_inf=""
	if ask_yesno "Should we attempt to automatically configure your hardware? [yes] " 0; then
		if ! auto_find_inf; then
			echo ""
			echo "No supported modem device found."
			if ${automode}; then
				echo "Re-run \"hsfconfig\" manually!"
				return 1
			fi
		fi
	fi

	if [ -z "${found_inf}" ]; then

		if ! choose_pcicomctrl; then
			echo ""
			ask_pci_id "Please enter your modem's PCI Vendor ID [${vendorid}]: "
			vendorid="${pci_id}"
			ask_pci_id "Please enter your modem's PCI Device ID [${deviceid}]: "
			deviceid="${pci_id}"
		fi

		choose_inf

		if [ -z "${found_inf}" ]; then
			return 1
		fi
	fi

	hsfconf_infFile="${found_inf}"
	hsfconf_vendorId="${vendorid}"
	hsfconf_deviceId="${deviceid}"

	case "${hsfconf_infFile}" in
	*linux_athens.inf)
		hsfconf_firmFile="${hsfetcdir}/cnxykf.hex"
		;;
	*)
		hsfconf_firmFile=""
		;;
	esac

	echo ""
	echo "Selected PCI VendorID=${vendorid} DeviceID=${deviceid}"
	echo "and INF file ${found_inf}"
}

preprocess_inf()
{
	HSFLINUXVERSION="${hsfversion}" awk < $1 '{
		while (1) {
			gsub(/\r/, ""); # strip any CRs
			gsub(/;.*$/, ""); # strip comments
			gsub(/^[ 	]*/, ""); # strip leading spaces
			gsub(/[ 	]*$/, ""); # strip trailing spaces
			gsub(/[ 	]*,[ 	]*/, ","); # strip spaces around commas
			gsub(/[ 	]*\\$/, "\\"); # strip spaces before cont. backslashes
			gsub(/[@]HSFLINUXVERSION[@]/, ENVIRON["HSFLINUXVERSION"]);

			if ($0 !~ /\\$/)
				break;

			gsub(/\\$/, ""); # strip continuation backslashes
			prevline=$0;
			getline;
			sub(/^/, prevline); # prepend previous line
		}
		if ($0 != "") # ignore empty lines
			print $0;
	}'
}

get_country_list()
{
	preprocess_inf $1 | \
		awk -F, '
			{ if ($0 !~ /^HKR,Country\\[0-9a-fA-F]*,(NAME|T35CODE)/)
				next;
			}
			{
				sub(/HKR,Country\\/, "");
				gsub(/ /, "_"); # convert spaces to underscores
				gsub(/\"/, ""); # remove double-quotes
			}
			$2 == "T35CODE" {t35code[$1]=$4","$5}
			$2 == "NAME" {name[$1]=$4}
			END { for (x in name)
				if(t35code[x] != "")
					print name[x]" "t35code[x]
			}' | \
		sort
}

rebuild_nvram()
{
	if [ -z "${hsfconf_vendorId}" -o -z "${hsfconf_deviceId}" ]; then
		echo ""
		echo "PCI VendorID and/or DeviceID not configured"
		return 1
	fi

	if [ -z "${hsfconf_countryT35c}" ]; then
		if [ -n "${hsfconf_countryName}" ]; then
			setcountry="${hsfconf_countryName}"
			configure_country
		fi
		if [ -z "${hsfconf_countryT35c}" ]; then
			echo ""
			echo "Country code not configured"
			return 1
		fi
	fi

	(
		echo "HKR,ID,VENDOR,,\"${hsfconf_vendorId}\""
		echo "HKR,ID,DEVICE,,\"${hsfconf_deviceId}\""
		echo "HKR,Country,Current,1,${hsfconf_countryT35c}"

		preprocess_inf ${hsfconf_infFile} | \
			grep '^HKR,' | \
			egrep -vi '^HKR,Country,(Current|Previous),'
	) >/tmp/hsfconfig.inf.$$
	res=$?

	if [ ${res} -eq 0 ]; then
		hsfinf2bin /tmp/hsfconfig.inf.$$ ${hsfnvramf}
		res=$?
	fi

	rm -f /tmp/hsfconfig.inf.$$

	if [ ${res} -eq 0 ]; then
		rm -f ${hsffirmwf}
		if [ -n "${hsfconf_firmFile}" ]; then
			hsffirm2bin "${hsfconf_firmFile}" ${hsffirmwf}
			res=$?
		fi
	fi

	return ${res}
}

ask_country()
{
	get_country_list ${hsfconf_infFile} > /tmp/hsfconf.sup.$$

	while true; do
		if [ -n "${setcountry}" ]; then
			answer="${setcountry}"
			setcountry=""
		elif ${automode}; then
			answer="${country}"
		else
			echo ""
			echo "Supported countries: "
			echo ""

			awk '{
					if (NR != 1) printf ", "
					if (NR % 5 == 0) print ""
					printf "%s",$1
				}
				END { print ""}' </tmp/hsfconf.sup.$$

			echo ""
			echo -n "Please enter your country name [$country]: "
			read answer
			case "${answer}" in
			"" | *"[ 	]"*)
				answer="${country}"
				;;
			*)
				answer="`echo \"${answer}\" | tr '[a-z ]' '[A-Z_]'`"
				;;
			esac
		fi

		if grep -i "^${answer} " /tmp/hsfconf.sup.$$ > /tmp/hsfconf.ncty.$$
		then
			read country countrycode < /tmp/hsfconf.ncty.$$
			rm -f /tmp/hsfconf.sup.$$ /tmp/hsfconf.ncty.$$
			return 0
		else
			echo ""
			echo "ERROR: Country \"${answer}\" is not supported."

			if ${automode}; then
				rm -f /tmp/hsfconf.sup.$$ /tmp/hsfconf.ncty.$$
				return 1
			fi
		fi
	done
}

autodetect_country()
{
	# Try to guess what country we're in, using the timezone settings

	localtime_size="`/bin/ls -lL /etc/localtime 2>/dev/null | awk '{print $5}'`"

	if ! [ ${localtime_size} -gt 0 ]; then
		return 1
	fi

	zoneinfo_dir=/usr/share/zoneinfo

	if [ ! -d ${zoneinfo_dir} -o ! -f ${zoneinfo_dir}/zone.tab ]; then
		return 1
	fi

	# The following maps ISO-3166 country codes to names used by HSF 
	iso_AU='AUSTRALIA'
	iso_AT='AUSTRIA'
	iso_BE='BELGIUM'
	iso_BG='BULGARIA'
	iso_CA='CANADA'
	iso_CN='CHINA'
	iso_HR='CROATIA'
	iso_CZ='CZECH'
	iso_DK='DENMARK'
	iso_FI='FINLAND'
	iso_FR='FRANCE'
	iso_DE='GERMANY'
	iso_GR='GREECE'
	iso_HK='HONG-KONG'
	iso_HU='HUNGARY'
	iso_IS='ICELAND'
	iso_IN='INDIA'
	iso_ID='INDONESIA'
	iso_IE='IRELAND'
	iso_IL='ISRAEL'
	iso_IT='ITALY'
	iso_JP='JAPAN'
	iso_KR='KOREA'
	iso_LU='LUXEMBOURG'
	iso_MY='MALAYSIA'
	iso_MX='MEXICO'
	iso_NL='NETHERLAND'
	iso_NZ='NEW_ZEALAND'
	iso_NO='NORWAY'
	iso_PH='PHILIPPINES'
	iso_PL='POLAND'
	iso_PT='PORTUGAL'
	iso_RU='RUSSIA'
	iso_SG='SINGAPORE'
	iso_SK='SLOVAKIA'
	iso_SI='SLOVENIA'
	iso_ZA='SOUTH_AFRICA'
	iso_ES='SPAIN'
	iso_SE='SWEDEN'
	iso_CH='SWITZERLAND'
	iso_TW='TAIWAN'
	iso_TH='THAILAND'
	iso_TR='TURKEY'
	iso_GB='UK'
	iso_AE='UNITED_ARAB_EMIRATES'
	iso_US='USA'

	(
		cd ${zoneinfo_dir} 2>/dev/null || return 1
		find . -type f -size "${localtime_size}"c -print | sed 's@^\./@@' | \
			while read file; do
				cmp -s /etc/localtime $file || continue
# in the egrep and sed regular expressions below, it is very important to
# have tabs, not spaces
				egrep "	$file(	.*|\$)" ${zoneinfo_dir}/zone.tab
			done | sed -n '/^[^#]/s/	.*//p' | sort | uniq | \
				while read code; do
					eval "echo \${iso_${code}}"
				done | sort | uniq
		return 0
	)
}

configure_country()
{
	# To configure the country type we need an inf file
	if [ -z "${hsfconf_infFile}" ]; then
		echo ""
		echo 1>&2 "$0: configure_country error: no inffile; run hsfconfig -hwtype first."
		return 1
	fi

	country="${setcountry}"
	if [ -z "${country}" -a -n "${hsfconf_countryName}" ]; then
		country="${hsfconf_countryName}"
	fi

	if [ -z "${country}" -o "${country}" = "AUTO" ]; then
		country="`autodetect_country | head -1`"
		echo ""
		if [ -z "${country}" ]; then
			echo "Unable to determine country, defaulting to \"USA\""
			country=USA
		else
			echo "Automatically guessed country (using timezone): \"${country}\""
		fi
		setcountry="${country}"
	fi

	ask_country || return 1

	hsfconf_countryName="${country}"
	hsfconf_countryT35c="${countrycode}"

	echo ""
	echo "Configuring modem for country: \"${hsfconf_countryName}\""
	echo "You may use the command \"hsfconfig --country\" to change this setting"
	return 0
}

use_prebuilt_modules()
{
	rm -f "/lib/modules/`uname -r`/misc/hsf"*.o
	ln -s "${hsflibdir}/modules/$1/hsf"*.o "/lib/modules/`uname -r`/misc/"
}

#manually_select_modules()
#{
#	echo "manually_select_modules: Not implemented yet"
#	exit 1
#
##	 adapt kern_version or mod versions if required
#}

files_present()
{
	for f; do
		if [ ! -f "${f}" ]; then
			missing_file="${f}"
			return 1
		fi
	done
}

remove_temp_kernel_tree()
{
	if [ -n "${KERNELORG}" ]; then
		rm -rf "${KERNELSRC}"
		KERNELSRC="${KERNELORG}"
		KERNELORG=""
		return 0
	fi

	return 1
}

create_temp_kernel_tree()
{
	KERNELORG="${KERNELSRC}"
	# get absolute pathname with cd/pwd to ensure that our temp dir
	# is on same filesystem due to use of hardlinks by cp -alL
	KERNELSRC="`(cd \"${KERNELORG}\" && /bin/pwd)`-hsftmp$$"
	rm -rf "${KERNELSRC}"
	if ! cp -alL "${KERNELORG}" "${KERNELSRC}"; then
		remove_temp_kernel_tree
		return 1
	fi

	return 0
}

kernelrebuild_instructions()
{
		echo ""
		echo "First, ensure that the proper kernel source and compiler packages"
		echo "from your distribution vendor and/or the community are installed."
		echo ""
		echo "The Linux kernel can then be reconfigured by running \"make menuconfig\""
		echo "under the kernel source directory (usually /usr/src/linux)."
		echo ""
		echo "Verify that the proper options for your system are selected,"
		echo "and that CONFIG_SMP (\"Symmetric multi-processing support\" under"
		echo "\"Processor type and features\") is disabled, as this driver is"
		echo "presently designed to work on single-processor machines."
		echo ""
		echo "Then compile and install your new kernel (for more information about"
		echo "this procedure, see the README file under the kernel source directory),"
		echo "reboot the system using the new kernel, and re-run \"hsfconfig\"."
}

check_kernel()
{
	if [ -r /proc/cpuinfo ] && ! grep -q '^flags[ 	]*:.*mmx' /proc/cpuinfo; then
		echo 1>&2 "HSF drivers require a processor supporting the MMX instruction set."
		return 1
	fi

	if [ -n "$OSKERNSMP" ]; then
		echo 1>&2 "HSF drivers currently don't support SMP (Symmetric Multi-Processing) kernels."
		kernelrebuild_instructions
		return 2
	fi

	return 0
}

right_version_file()
{
	_versionf="$1"

	[ -n "${KERNELVER}" ] || KERNELVER="`uname -r`"

	FILEKERNELVER="`echo UTS_RELEASE | gcc -E -I"${KERNELSRC}/include" -include "${_versionf}" - | grep '^"' | tr -d '"	 '`"

	[ "${KERNELVER}" = "${FILEKERNELVER}" ]
}

recompile_modules()
{
	KERNELVER="`uname -r`"
	KERNELSRC=/usr/src/linux
	KERNELORG=""
	if [ -d /lib/modules/${KERNELVER}/build/include ]; then
		KERNELSRC=/lib/modules/${KERNELVER}/build
	elif [ ! -d "${KERNELSRC}" -a -d "/usr/local/src/linux" ]; then
		KERNELSRC=/usr/local/src/linux
	fi
	if ! $automode; then
		echo ""
		echo "Where is the directory of C header files that match your running kernel?"
		echo -n "[${KERNELSRC}] "
		read answer
		case "${answer}" in
		"")
			;;
		*)
			KERNELSRC="${answer}"
			;;
		esac
	fi

	if ! files_present \
		"${KERNELSRC}/include/linux/autoconf.h" \
		"${KERNELSRC}/include/linux/version.h"; then
		echo ""
		echo "WARNING: missing file ${missing_file}"
		suspect_tree=true
	elif ! right_version_file "${KERNELSRC}/include/linux/version.h"; then
		echo ""
		echo "WARNING: the kernel version (${FILEKERNELVER}) defined in"
		echo "${KERNELSRC}/include/linux/version.h"
		echo "does not match the currently running kernel (${KERNELVER})"
		echo "The cause of this problem is an incorrect kernel source path."
		echo "Please check that ${KERNELSRC} points to the right tree."
		suspect_tree=true
	else
		suspect_tree=false
	fi

	if ${suspect_tree}; then
		echo "The cause of this problem is usually a missing or misconfigured"
		echo "kernel source tree (and sometimes an incorrect directory or symbolic link)."
		# SuSE has copies of autoconf.h and version.h available under /boot
		if [ -d "${KERNELSRC}/include/linux" -a -f /boot/vmlinuz.autoconf.h -a -f /boot/vmlinuz.version.h ] && right_version_file /boot/vmlinuz.version.h; then
			echo ""
			echo "However, proper /boot/vmlinuz.{autoconf.h,version.h} were found."
			if ask_yesno "Would you like to try using them (in a temporary kernel tree)? [yes] " 0; then
				if ! create_temp_kernel_tree; then
					echo ""
					echo "Unable to create temporary kernel tree"
					kernelrebuild_instructions
					return 1
				fi
				rm -f "${KERNELSRC}/include/linux/.config" \
						"${KERNELSRC}/include/linux/autoconf.h" \
						"${KERNELSRC}/include/linux/version.h"
				if ! cp -p /boot/vmlinuz.autoconf.h "${KERNELSRC}/include/linux/autoconf.h" || \
					! cp -p /boot/vmlinuz.version.h "${KERNELSRC}/include/linux/version.h"; then
					echo ""
					echo "Unable to copy files to temporary kernel tree"
					remove_temp_kernel_tree
					kernelrebuild_instructions
					return 1
				fi

				# we don't really need .config but it can't hurt if available
				if [ -f /boot/vmlinuz.config ]; then
					cp -p /boot/vmlinuz.config "${KERNELSRC}/include/linux/.config"
				fi
			fi
		fi
	fi

	# are we ok now?
	if [ ! -f "${KERNELSRC}/include/linux/version.h" -o ! -f "${KERNELSRC}/include/linux/autoconf.h" ]; then
		remove_temp_kernel_tree
		kernelrebuild_instructions
		return 1
	fi

	recomplogf=/tmp/hsfconfig-recomp.log.$$

	# set FILEKERNELVER if it isn't already
	[ -n "${FILEKERNELVER}" ] || right_version_file "${KERNELSRC}/include/linux/version.h"

	echo ""
	echo "Re-compiling HSF modules for kernel ${FILEKERNELVER}, using source directory"
	echo "${KERNELSRC}. Please wait.."

	(cd ${hsflibdir}/modules && make "KERNELSRC=${KERNELSRC}" clean minstall) \
		> ${recomplogf} 2>&1

	res=$?

	(cd ${hsflibdir}/modules && make "KERNELSRC=${KERNELSRC}" clean) >/dev/null 2>&1

	remove_temp_kernel_tree

	if [ $res -eq 0 ]; then
		rm -f ${recomplogf}
		echo "Re-compilation and installation of HSF modules succeeded."
		return 0
	else
		echo ""
		echo "ERROR: Module re-compilation and installation failed!"
		echo "Please examine the log file \"${recomplogf}\" to determine why."
		return 1
	fi
}

modules_exist()
{
	files_present "/lib/modules/`uname -r`/misc/hsf"*.o
}

hsfscrdmp_exists()
{
	files_present "/lib/modules/`uname -r`/misc/hsfscrdmp.o"
}

remove_kernel_modules()
{
	if modules_exist; then
		echo ""
		echo "Removing HSF drivers under /lib/modules/`uname -r`/misc/"
		rm -f "/lib/modules/`uname -r`/misc/hsf"*.o
	fi
}

choose_kernel()
{
	ihave=${OSKERNNAME}-${OSKERNVERS}-${OSKERNARCH}-${OSDISTNAME}-${OSDISTVERS}${OSKERNSMP}${OSKERNMODV}


	if modules_exist; then
		echo ""
		echo "Warning: existing HSF modules found under /lib/modules/`uname -r`/misc/"
		if ask_yesno "Would you like to keep using them? [no] " 1; then
			return 0
		fi
	fi

	if [ -d "${hsflibdir}/modules/${ihave}" ]; then
		echo ""
		echo "Pre-built kernel modules for your ${ihave} system were found."
		if ask_yesno "Would you like to use them? [yes] " 0; then
			if use_prebuilt_modules "${ihave}"; then
				return 0
			fi
		fi
	else
		echo ""
		echo "No pre-built HSF modules are available for your exact kernel:"
		echo "		${ihave}"
	fi

#	if ! ${automode}; then
#		echo ""
#		echo "If a C compiler and proper kernel header files are installed on your system"
#		echo "it is preferable to re-compile the modules."
#		echo ""
#		echo "Otherwise, you may manually select pre-build modules for a similar system"
#		echo "(this is AT YOUR OWN RISK since none may be suitable for your running kernel)"
#		echo ""
#	else
		echo ""
		echo "Assuming that a C compiler and proper kernel header files are present"
		echo "on your system, we will now attempt to re-compile the modules."
#		echo ""
#		echo "Since we are running in non-interactive mode it is not possible to"
#		echo "manually select alternative pre-build binary modules at this stage."
#		echo "If the re-compilation fails you may do that later, using \"hsfconfig\"".
#	fi

#	if ask_yesno "Would you like to try to re-compile the HSF modules? [yes] " 0; then
		if recompile_modules; then
				return 0
		fi
#	fi

#	if ! ${automode}; then
#		manually_select_modules
#	else
		return 1
#	fi
}

choose_major()
{
	device=$1
	shift

	first=240
	max=250

	major=${first}

	while [ ${major} -ne ${max} ]; do
		excluded=false
		for exclude in $*; do
			if [ ${major} -eq ${exclude} ]; then
				excluded=true
				break
			fi
		done

		if ! ${excluded} && \
			! grep -q "^ *${major} " /proc/devices && \
			! sed -e 's/#.*//g' -e '/hsfserial/d' -e '/hsfscrdmp/d' ${modulesconf} | grep -q "^alias[ 	][ 	]*char-major-${major}"
			then
				break
		fi

		major="`expr ${major} + 1`"
	done

	if [ ${major} -ne ${max} ]; then
		echo ${major}
		return 0
	else
		echo 1>&2 "$0: Cannot find major device number for ${device} in range ${first}-${max}"
		return 1
	fi
}

devfsd_running()
{
	ps -e | grep -q 'devfsd$'
}

configure_serial()
{
	hsftty_major="`choose_major hsfserial`"

	if [ -z ${hsftty_major} ]; then
		exit 1
	fi

	hsfcua_major="`choose_major hsfserial_callout ${hsftty_major}`"
	if [ -z ${hsfcua_major} ]; then
		exit 1
	fi

	if hsfscrdmp_exists; then
		scrdevicename=/dev/hsfscr
		dmpdevicename=/dev/hsfdmp

		hsfscrdmp_major="`choose_major hsfscrdmp ${hsftty_major} ${hsfcua_major}`"
		if [ -z ${hsfscrdmp_major} ]; then
			exit 1
		fi
	fi

	if (
		sed -e '/^alias.*hsfserial/d' -e '/^options.*hsfserial/d' \
			-e '/^alias.*hsfscrdmp/d' -e '/^options.*hsfscrdmp/d'

		echo "alias /dev/ttyHSF* hsfserial"
		echo "alias char-major-${hsftty_major} hsfserial"

		echo "alias /dev/ttyCUA* hsfserial"
		echo "alias char-major-${hsfcua_major} hsfserial"
		echo "alias /dev/modem hsfserial"
		echo "options hsfserial serialmajor=${hsftty_major} calloutmajor=${hsfcua_major}"

		[ -n "${scrdevicename}" ] && echo "alias ${scrdevicename} hsfscrdmp"
		[ -n "${dmpdevicename}" ] && echo "alias ${dmpdevicename} hsfscrdmp"
		if [ -n "${hsfscrdmp_major}" ]; then
			echo "alias char-major-${hsfscrdmp_major} hsfscrdmp"
			echo "options hsfscrdmp scrdmpmajor=${hsfscrdmp_major}"
		fi

	) < ${modulesconf} > ${modulesconf}.$$; then
		if ! cp ${modulesconf}.$$ ${modulesconf}; then
			rm -f ${modulesconf}.$$
			exit 1
		fi
	fi

	rm -f ${modulesconf}.$$

	depmod -a

	ttydevicename=/dev/ttyHSF0
	cuadevicename=/dev/cuaHSF0

	if [ -f /etc/devfsd.conf ] && (
		sed -e '/^REGISTER.*ttyHSF/d'
		echo 'REGISTER	^ttyHSF0$ CFUNCTION GLOBAL symlink $devname modem'
	) < /etc/devfsd.conf > /etc/devfsd.conf.$$; then
		if ! cp /etc/devfsd.conf.$$ /etc/devfsd.conf; then
			rm -f /etc/devfsd.conf.$$
		fi
		killall -HUP devfsd 2>/dev/null
	fi

	if ! devfsd_running; then
		hsf_minor=64

		rm -f ${ttydevicename} ${cuadevicename}

		if [ -n "${scrdevicename}" ]; then
			rm -f ${scrdevicename}
			mknod -m 600 ${scrdevicename} c ${hsfscrdmp_major} 0 || exit 1
		fi

		if [ -n "${dmpdevicename}" ]; then
			rm -f ${dmpdevicename}
			mknod -m 600 ${dmpdevicename} c ${hsfscrdmp_major} 1 || exit 1
		fi

		mknod -m 666 ${ttydevicename} c ${hsftty_major} ${hsf_minor} || exit 1
		mknod -m 666 ${cuadevicename} c ${hsfcua_major} ${hsf_minor} || exit 1

		if [ -h /dev/modem -a "`/bin/ls -l /dev/modem 2>/dev/null | awk '{print $11}'`" = ${ttydevicename} ]; then
			rm -f /dev/modem
		fi

		if [ -e /dev/modem ]; then
			rm -f /dev/modem.old
			mv /dev/modem /dev/modem.old

			echo "WARNING: renamed existing /dev/modem to /dev/modem.old:"
			echo ""
			/bin/ls -l /dev/modem.old
		fi

		ln -s ${ttydevicename} /dev/modem
	fi

	echo ""
	echo "Your HSF modem should now be accessible as ${ttydevicename}"

	if [ -h /dev/modem -a "`/bin/ls -l /dev/modem 2>/dev/null | awk '{print $11}'`" = ${ttydevicename} ]; then
		echo "or through the symbolic link /dev/modem."
	fi

	echo ""

	oldnames="/dev/cnxt0 /dev/hsf0"

	for oldname in ${oldnames}; do
		if [ -e ${oldname} ]; then
			echo "WARNING: old device ${oldname} still exists; you should probably remove it"
		fi
	done
}

dump_cmd()
{
	echo ""
	echo "${dumpprefix} $*"
	$@
}

dump_file()
{
	dump_cmd cat $@
}

dump_diagnostics()
{
	dumpprefix="+"

	echo ""
	echo "Dumping system diagnostic information..."

	(
		dump_cmd date
		dump_cmd uname -a
		dump_cmd hsfsysid
		dump_cmd hsfconfig --version

		dump_file /proc/version
		dump_file /proc/cpuinfo

		dump_cmd lspci --version

		dump_cmd lspci -n -vvv

		dump_file /proc/pci

		for p in cmdline uptime \
			devices misc filesystems \
			interrupts iomem ioports dma \
			mtrr \
			partitions mounts swaps \
			meminfo slabinfo stat \
			apm
		do
			dump_file /proc/${p}
		done

		dump_file /proc/modules
		dump_file "${modulesconf}"

		dump_cmd dmesg

		dump_file /proc/tty/drivers
		for f in /proc/tty/driver/*; do
			if [ -r "${f}" ]; then
				dump_cmd cat -v "${f}"
			fi
		done
		dump_file /proc/tty/ldiscs

		dump_cmd ls -l '/dev/modem*'
		dump_cmd ls -l '/dev/*HSF*'
		dump_cmd ls -l '/dev/*cnxt*'
		dump_cmd ls -l '/dev/*hsf*'
		dump_cmd ls -l '/dev/tty*'
		dump_cmd ls -l '/dev/tts*'

		dump_cmd ls -lR /etc/.
		dump_cmd ls -lR "${hsflibdir}/."
		dump_cmd ls -lR /lib/modules/.
		dump_cmd ls -lR /boot/.

		dump_cmd ls -l /usr/include/. /usr/src/.
		
		dump_cmd ls -l /usr/src/linux/include/.
		dump_cmd ls -l /usr/src/linux/include/linux/.
		dump_cmd ls -l /usr/src/linux/include/asm-i386/.

		dump_file /lib/modules/`uname -r`/build/.config
		dump_file /lib/modules/`uname -r`/build/include/linux/version.h
		dump_file /lib/modules/`uname -r`/build/include/linux/autoconf.h
		dump_file /lib/modules/`uname -r`/build/include/linux/autoconf-up.h
		dump_file /lib/modules/`uname -r`/build/include/linux/rhconfig.h
		dump_file /lib/modules/`uname -r`/build/include/linux/modules/ksyms.ver

		dump_file /usr/src/linux/.config
		dump_file /usr/src/linux/include/linux/version.h
		dump_file /usr/src/linux/include/linux/autoconf.h
		dump_file /usr/src/linux/include/linux/autoconf-up.h
		dump_file /usr/src/linux/include/linux/rhconfig.h
		dump_file /usr/src/linux/include/linux/modules/ksyms.ver

		dump_file /usr/include/linux/version.h
		dump_file /usr/include/linux/autoconf.h
		dump_file /usr/include/linux/autoconf-up.h
		dump_file /usr/include/linux/rhconfig.h
		dump_file /usr/include/linux/modules/ksyms.ver

		dump_file /boot/vmlinuz.version.h
		dump_file /boot/vmlinuz.autoconf.h
		dump_file /boot/vmlinuz.config

		dump_file /boot/kernel.h

		dump_cmd insmod -V

		dump_cmd depmod -e -a

		if dmesg | grep -qi 'panic'; then
			dump_cmd dmesg | ksymoops
		fi

		dump_file /proc/ksyms

		dump_cmd env

		dump_cmd set

		for f in /etc/*_version /etc/*-version /etc/*-release; do
			if [ -r "${f}" ]; then
				dump_cmd cat -v "${f}"
			fi
		done

		dump_cmd ps -efw

		dump_cmd sh --version
		dump_cmd sed --version
		dump_cmd awk --version

		dump_cmd tar --version

		dump_cmd rpm --version
		dump_cmd rpm --showrc
		dump_cmd rpm --query --all

	) > "${hsfdiagfile}" 2>&1

	if [ -f "${hsfdiagfile}" ]; then
		echo ""
		echo "System diagnostic information has been saved to the file:"
		echo ""
		ls -l ${hsfdiagfile}
		echo ""
		echo "As it could contain private information about this system, we recommend that"
		echo "you verify its contents before sending it to third parties."
	fi

}

show_intro()
{
	echo "Linux HSF softmodem drivers, version ${hsfversion}"

	case "${hsfversion}" in
	*beta*)
		(
		echo ""
		echo "WARNING: this is an EXPERIMENTAL BETA VERSION of the HSF drivers for Linux."
		echo "USE AT YOUR OWN RISK! See the file ${hsflibdir}/LICENSE for details."
		) 1>&2
		;;
	esac

	case "${hsfversion}" in
	*mbsi*)
		(
		echo ""
		echo "Conexant Systems neither distributes nor maintains this package."
		echo "PLEASE DO NOT CONTACT CONEXANT REGARDING THIS SOFTWARE!!!"
		echo "If you require assistance or need more information, go to:"
		echo "              http://www.mbsi.ca/hsflinux"
		) 1>&2
		;;
	esac
}


show_usage_and_exit()
{
	show_intro

	cat 1>&2 << EOT

Usage: `basename $0` [options]

  -k, --kernel                 Configure and install HSF kernel modules
  -t, --hwtype                 Configure hardware type and parameters
  -c, --country[=<name>]       Configure country, to <name> if specified
                               (Use country name "auto" to infer from timezone)
  -s, --serial                 Install HSF serial port devices

  -n, --nvram                  Rebuild ${hsfnvramf} using current config

  -i, --info                   Show current configuration parameters

  -d, --dumpdiag[=<file>]      Dump system diagnostic information to <file>
                               (default: ${hsfdiagfile})

  -a, --auto                   Run in automatic mode, without prompting user
  -r, --remove                 Remove HSF kernel modules

  -h, --help                   This small usage guide
  -V, --version                Show HSF version

Default options if none selected: --kernel --hwtype --country --serial
EOT
	exit 1
}

# end of functions; execution starts here

PATH=/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/local/bin
export PATH

umask 022

hsflibdir="/usr/lib/hsf"
hsfetcdir="/etc/hsf"
hsfinfdir="/etc/hsf/inf"
hsfnvramf="/etc/hsf/nvram.bin"
hsffirmwf="/etc/hsf/firmware.bin"
hsfversion="4.06.06.02"

hsfdiagfile="/tmp/hsfdiag.txt"

if [ -f /etc/modules.conf ]; then
	modulesconf=/etc/modules.conf
elif [ -f /etc/conf.modules ]; then
	modulesconf=/etc/conf.modules
fi

TEMP=`getopt -a -o ktc::snid::arhV --long kernel,hwtype,country::,serial,nvram,info,dumpdiag::,auto,remove,help,version -n "\`basename $0\`" -- "$@"`
if [ $? != 0 ]; then
	show_usage_and_exit
fi

eval set -- "$TEMP"

automode=false
do_cfgkernel=false
do_cfghwtype=false
do_cfgnvram=false
do_cfgserial=false
do_infocfg=false
do_dumpdiag=false
do_cfgremove=false
do_cfgcountry=false
setcountry=""
setdiagfile=""

explicitopt=false

while true ; do
	case "$1" in
		-a|--auto) automode=true; shift ;;
		-c|--country) 
			explicitopt=true
			do_cfgcountry=true
			if [ -n "$2" ]; then
				setcountry="`echo \"$2\" | tr '[a-z ]' '[A-Z_]'`"
			fi
			shift 2
			;;
		-k|--kernel) explicitopt=true; do_cfgkernel=true; shift ;;
		-t|--hwtype) explicitopt=true; do_cfghwtype=true; shift ;;
		-n|--nvram)  explicitopt=true; do_cfgnvram=true;  shift ;;
		-s|--serial) explicitopt=true; do_cfgserial=true; shift ;;
		-i|--info)   explicitopt=true; do_infocfg=true; shift ;;
		-d|--dumpdiag)
			explicitopt=true
			do_dumpdiag=true
			setdiagfile="$2"
			shift 2
			;;
		-r|--remove) explicitopt=true; do_cfgremove=true; shift ;;
		-h|--help) show_usage_and_exit; shift ;;
		-V|--version) echo "${hsfversion}"; exit 0;;
		--) shift ; break ;;
		*) echo "Internal error!" ; exit 1 ;;
	esac
done

# Accomodate --country <name> as equivalent to --country=<name>
if ${do_cfgcountry} && [ $# -ge 1 -a -z "${setcountry}" ]; then
	setcountry="`echo \"$1\" | tr '[a-z ]' '[A-Z_]'`"
	shift
fi

# Accomodate --dumpdiag <file> as equivalent to --dumpdiag=<file>
if ${do_dumpdiag} && [ $# -ge 1 -a -z "${setdiagfile}" ]; then
	setdiagfile="$1"
	shift
fi

if [ $# -ne 0 ]; then
	echo 1>&2 "$0: extraneous arguments: $*"
	show_usage_and_exit
fi

# If nothing explicitely specified, configure everything
if ! ${explicitopt}; then
	do_cfgkernel=true
	do_cfghwtype=true
	do_cfgcountry=true
	do_cfgserial=true
fi

if ! ${do_cfgremove}; then
	show_intro
fi

# before doing anything, try to unload any hsf drivers possibly running
hsfstop >/dev/null 2>&1

. `which hsfsysid` >/dev/null

load_config || exit $?

if ${do_cfgremove}; then
	remove_kernel_modules || exit $?
fi

if ${do_cfgkernel}; then
	check_kernel || exit $?
	choose_kernel || exit $?
fi

if ${do_cfghwtype}; then
	configure_inf || exit $?
	do_cfgnvram=true
fi

if ${do_cfgcountry}; then
	configure_country || exit $?
	do_cfgnvram=true
fi

if ${do_cfgnvram}; then
	rebuild_nvram || exit $?
	save_config || exit $?
fi

if ${do_cfgserial}; then
	configure_serial || exit $?
fi

if ${do_infocfg}; then
	show_config || exit $?
fi

if ${do_dumpdiag}; then
	if [ -n "${setdiagfile}" ]; then
		hsfdiagfile="${setdiagfile}"
	fi
	dump_diagnostics || exit $?
fi
