#!/bin/bash

set -o nounset
set -o errexit

REPO_URI="http://github.com/Hexxeh/rpi-firmware"

UPDATE_SELF=${UPDATE_SELF:-1}
UPDATE_URI="https://github.com/Hexxeh/rpi-update/raw/master/rpi-update"

if [[ ${BOOT_PATH:-"unset"} == "unset" && ${ROOT_PATH:-"unset"} != "unset" ]] ||
[[ ${BOOT_PATH:-"unset"} != "unset" && ${ROOT_PATH:-"unset"} == "unset" ]]; then
	echo " *** You need to specify both ROOT_PATH and BOOT_PATH, or neither"
	exit 1
fi

BRANCH=${BRANCH:-"master"}
ROOT_PATH=${ROOT_PATH:-"/"}
BOOT_PATH=${BOOT_PATH:-"/boot"}
SKIP_KERNEL=${SKIP_KERNEL:-0}
RPI_UPDATE_UNSUPPORTED=${RPI_UPDATE_UNSUPPORTED:-0}
FW_REPO="${REPO_URI}.git"
FW_REPOLOCAL="${ROOT_PATH}/root/.rpi-firmware"
FW_PATH="${BOOT_PATH}"
FW_MODPATH="${ROOT_PATH}/lib/modules"
FW_REV=${1:-""}
GITCMD="git --git-dir=\"${FW_REPOLOCAL}/.git\" --work-tree=\"${FW_REPOLOCAL}\""
[ "${RPI_UPDATE_UNSUPPORTED}" -ne 0 ] && echo -e "You appear to be trying to update firmware on an incompatible distribution. To force update, run the following:\nsudo -E RPI_UPDATE_UNSUPPORTED=0 ./rpi-update" && exit 1

function update_self() {
	echo " *** Performing self-update"
	_tempFileName="$0.tmp"

	if ! wget --output-document="${_tempFileName}" "${UPDATE_URI}"; then
		echo " !!! Failed to download update for rpi-update!"
		echo " !!! Make sure you have ca-certificates installed and that the time is set correctly"
		exit 1
	fi

	OCTAL_MODE=$(stat -c '%a' "$0")
	if ! chmod ${OCTAL_MODE} "${_tempFileName}" ; then
		echo " !!! Failed: Error while trying to set mode on ${_tempFileName}"
		exit 1
	fi

	cat > /root/.updateScript.sh << EOF
	#!/bin/bash
	if mv "${_tempFileName}" "$0"; then
		rm -- "\$0"
		exec env UPDATE_SELF=0 /bin/bash "$0" "${FW_REV}"
	else
		echo " !!! Failed!"
	fi
EOF

	echo " *** Relaunching after update"
	exec /bin/bash /root/.updateScript.sh
}

function update_modules {
	if [[ ${SKIP_KERNEL} -eq 0 ]]; then
		echo " *** Updating modules"
		cp -vR "${FW_REPOLOCAL}/modules/"* "${FW_MODPATH}/"
		find "${FW_REPOLOCAL}/modules" -mindepth 1 -maxdepth 1 -type d | while read DIR; do
			echo " *** depmod $(basename "${DIR}")"
			depmod -b "${ROOT_PATH}" -a $(basename "${DIR}")
		done
	else
		echo " *** As requested, not updating modules"
	fi
}

function update_sdk {
	echo " *** Updating SDK"

	ELFOUTPUT=$(readelf -a "${ROOT_PATH}/bin/bash")
	if [ "${ELFOUTPUT}" != "${ELFOUTPUT/VFP_args/}" ]; then
		echo " *** Using HardFP libraries"
		cp -vR "${FW_REPOLOCAL}/vc/hardfp/"* "${ROOT_PATH}/"
	else
		echo " *** Using SoftFP libraries"
		cp -vR "${FW_REPOLOCAL}/vc/softfp/"* "${ROOT_PATH}/"
	fi
	cp -vR "${FW_REPOLOCAL}/vc/sdk/"* "${ROOT_PATH}/"
}

function update_firmware {
	echo " *** Updating firmware"
	rm -rf ${FW_PATH}/*.elf
	rm -rf ${FW_PATH}/*.bin
	cp -v ${FW_REPOLOCAL}/*.elf "${FW_PATH}/"
	cp -v ${FW_REPOLOCAL}/*.bin "${FW_PATH}/"
	cp -v ${FW_REPOLOCAL}/*.dat "${FW_PATH}/"
	if [[ ${SKIP_KERNEL} -eq 0 ]]; then
		cp -v "${FW_REPOLOCAL}/"*.img "${FW_PATH}/"
	else
		echo " *** As requested, not updating kernel"
	fi
}

function finalise {
	if [[ -f "${FW_PATH}/arm192_start.elf" ]]; then
		echo " *** Setting 192M ARM split"
		cp -v "${FW_PATH}/arm192_start.elf" "${FW_PATH}/start.elf"
	fi
	echo " *** Running ldconfig"
	ldconfig -r "${ROOT_PATH}"
	if [[ ${FW_REV} == "" ]]; then
		echo " *** Storing current firmware revision"
		eval ${GITCMD} rev-parse ${BRANCH} > "${FW_PATH}/.firmware_revision"
  else
    echo ${FW_REV} > "${FW_PATH}/.firmware_revision"
  fi
	echo " *** Syncing changes to disk"
	sync
}

function download_repo {
	echo " *** Setting up firmware (this may take a few minutes)"
	mkdir -p "${FW_REPOLOCAL}"
	set +e
	git clone "${FW_REPO}" "${FW_REPOLOCAL}" --depth=1 --branch ${BRANCH}
	RETVAL=$?
	set -e
	if [[ ${RETVAL} -ne 0 ]]; then
		echo " !!! Failed to download new firmware files"
		exit 1
	fi
}

function update_repo {
	# updating a depth=1 git tree doesn't work, so just kill it and clone a new one
	rm -rf ${FW_REPOLOCAL}
	download_repo
}

function do_backup {
	echo " *** Backing up files"
	rm -rf "${FW_PATH}.bak"
	cp -va "${FW_PATH}" "${FW_PATH}.bak"
	rm -rf "${FW_MODPATH}.bak"
	cp -va "${FW_MODPATH}" "${FW_MODPATH}.bak"
}

function do_update {
	update_firmware
	update_modules
	update_sdk
	finalise
	echo " *** If no errors appeared, your firmware was successfully $1"
	if [[ "${ROOT_PATH}" == "/" ]]; then
		echo " *** A reboot is needed to activate the new firmware"
	fi
}

function download_rev {
	echo " *** Downloading specific firmware revision (this will take a few minutes)"
	mkdir -p "${FW_REPOLOCAL}"
	wget "${REPO_URI}/tarball/${FW_REV}" -O "${FW_REPOLOCAL}/${FW_REV}.tar.gz"
	tar xzf "${FW_REPOLOCAL}/${FW_REV}.tar.gz" -C "${FW_REPOLOCAL}" --strip-components=1
	rm "${FW_REPOLOCAL}/${FW_REV}.tar.gz"
}

if [[ ${EUID} -ne 0 ]]; then
	echo " !!! This tool must be run as root"
	exit 1
fi

echo " *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS"

if [[ ${UPDATE_SELF} -ne 0 ]]; then
	update_self
fi

if [[ ! -d "${FW_PATH}" ]]; then
	echo " !!! ${FW_PATH} doesn't exist"
	exit 1
fi
if [[ ! -d "${FW_MODPATH}" ]]; then
	echo " !!! ${FW_MODPATH} doesn't exist"
	exit 1
fi

command -v git >/dev/null 2>&1 || {
	echo " !!! This tool requires you have Git installed, please install it first"
	echo "     In Debian, try: sudo apt-get install git-core"
	echo "     In Arch, try: pacman -S git"
	exit 1
}

command -v readelf >/dev/null 2>&1 || {
	echo " !!! This tool requires you have readelf installed, please install it first"
	echo "     In Debian, try: sudo apt-get install binutils"
	echo "     In Arch, try: pacman -S binutils"
	exit 1
}

echo " *** ARM/GPU split is now defined in /boot/config.txt using the gpu_mem option!"
if [[ ${FW_REV} != "" ]]; then
	download_rev
	do_update "updated to revision ${FW_REV}"
elif [[ -f "${FW_REPOLOCAL}/.git/config" ]]; then
	# ask git server version before spending time cloning
	GITREV=$(git ls-remote -h ${REPO_URI} refs/heads/${BRANCH} | awk '{print $1}')
	if [[ -f "${FW_PATH}/.firmware_revision" ]] && [[ $(cat "${FW_PATH}/.firmware_revision") == "$GITREV" ]]; then
		echo " *** Your firmware is already up to date"
		# no changes made, nothing to finalise
		# finalise
	else
		update_repo
		do_update "updated"
	fi
else
	echo " *** We're running for the first time"
	download_repo
	do_backup
	do_update "setup"
fi
