#!/bin/sh

mount -nt proc proc proc
mount -nt sysfs sysfs sys

get_of_alias () {
	if test -e /proc/device-tree/aliases/$1 ; then
		cat /proc/device-tree/aliases/$1
	else
		echo ""
	fi
}

# Translate an openfirmware device path into its fully expanded form, based on what
# openfirmware exported to us.
resolve_of_path () {
	case $1 in
	    /*)
		# A leading slash means it's an absolute device path
		REALPATH=$1
		;;
	    *)
		# Without, we assume it's a device alias
		# Resolve the initial alias portion - you can specify an alias
		# with trailing child node specifiers.
		REALPATH=$(get_of_alias $(echo $1 | sed 's/\/.*$//'))
		REALPATH=${REALPATH}$(echo $1 | sed -n 's/^[^\/]\+\//\//p')
		;;
	esac
	# openfirmware's a little funky with ata disks - it expands to 'disk@0'
	# for a boot device path, but the of tree doesn't have separate nodes for
	# separate disks - if there could be a disk there, it's just 'disk'...
	# ... might do this with scsi disks as well? I forget.
	REALPATH=$(echo $REALPATH | sed 's/\(\/ata-[0-9]\+\(\@[0-9a-f]\+\)\?\/disk\)\@0$/\1/')
	# make use of the info openfirmware exported to us, which the kernel
	# graciously provides under /proc/device-tree...
	readlink -f /proc/device-tree$REALPATH | sed 's/^\/proc\/device-tree//'
}

# get the device tree base paths for the pluggable buses, for later comparison.
# this is all specific to openfirmware on the powerpc, so don't expect it to
# work elsewhere. also, most openfirmware implementations provide the hardcoded
# aliases, but the hardware isn't necessarily there to back them up. caveat
# emptor, anyone?
USB0=$(resolve_of_path usb0)
if [ "x$USB0" = "x" ] ; then
	USB0=NO_VALID_OF_DEV
fi

USB1=$(resolve_of_path usb1)
if [ "x$USB1" = "x" ] ; then
	USB1=NO_VALID_OF_DEV
fi

FW=$(resolve_of_path fw)
if [ "x$FW" = "x" ] ; then
	FW=NO_VALID_OF_DEV
fi

FW1=$(resolve_of_path fw1)
if [ "x$FW1" = "x" ] ; then
	FW1=NO_VALID_OF_DEV
fi

FWX=$(resolve_of_path fwx)
if [ "x$FWX" = "x" ] ; then
	FWX=NO_VALID_OF_DEV
fi

# make sure the boot path is in its fully expanded form, but without
# the partition and filename portions.
BOOTPATH=$(resolve_of_path $(echo $(cat /proc/device-tree/chosen/bootpath) | sed -e 's/:[0-9]\+\(,\\.*\)\?$//'))
# see if the boot path falls into any category we care about
case $BOOTPATH in
    # if we're booting from a usb drive
    ${USB0}*|${USB1}*)
	# try to load host controller drivers, incase they aren't already
	# present. then we have to deduce the sysfs path that the drive
	# will (or should, anyway) show up at.
	modprobe ohci-hcd > /dev/null 2>&1
	modprobe ehci-hcd > /dev/null 2>&1
	modprobe usb-storage > /dev/null 2>&1
	modprobe sd_mod > /dev/null 2>&1
	modprobe sr_mod > /dev/null 2>&1
	# we know what the bus numbers will map to - or at least should map to,
	# every mac i've seen has the aliases in bus order anyway
	case $BOOTPATH in
	    ${USB0}*)
		BUSNUM=1
		BASEPATH=$USB0
		;;
	    ${USB1}*)
		BUSNUM=2
		BASEPATH=$USB1
		;;
	esac
	# get just the path elements past the node for the host controller
	USBPATH=$(echo $BOOTPATH | sed "s/$(echo $BASEPATH | sed 's/\//\\\//g')\///")
	# assemble a usb device designator, containing thr port numbers according
	# to any intervening hub devices.
	while [ "x$USBPATH" != "x" ] ; do
		PATHELEM=$(echo $USBPATH | sed 's/\/.*$//')
		USBPATH=$(echo $USBPATH | sed -n 's/^[^\/]\+\///p')
		DEVNUM=$(echo $PATHELEM | sed 's/^.*@//')
		case $PATHELEM in
		    hub\@*)
			DESIGNATOR="${DESIGNATOR}${DEVNUM}."
			;;
		    disk\@*)
			DESIGNATOR="${DESIGNATOR}${DEVNUM}"
			;;
		    *)
			echo "uhh... what's a ${PATHELEM}?"
			;;
		esac
	done
	# still dunno how to get the config number or interface number, but
	# that's enough information to be reasonably correct...
	SYSFSPATH="/sys/bus/usb/drivers/usb-storage/${BUSNUM}-${DESIGNATOR}:*"
	echo "SYSFSPATH is $SYSFSPATH"
	;;
    # or from a firewire/ieee1394 drive
    ${FW}*|${FW1}*|${FWX}*)
	# anything we would actually really care about is just about
	# guaranteed to be hanging off an OHCI1394-compliant controller.
	modprobe ohci1394 > /dev/null 2>&1
	modprobe sbp2 > /dev/null 2>&1
	modprobe sd_mod > /dev/null 2>&1
	modprobe sr_mod > /dev/null 2>&1
	# figure out what the /sysfs path for the device should look like,
	# based on the OF device path we got
	GUID=$(echo $BOOTPATH | sed 's/^.*\/node@\([0-9a-f]\+\)\/.*$/\1/')
	DISKNUM=$(echo $BOOTPATH | sed 's/^.*\/disk@\([0-9a-f]\+\)\(:.*\)\?$/\1/')
	SYSFSPATH=/sys/bus/ieee1394/drivers/sbp2/${GUID}-${DISKNUM}
	;;
    # don't have to care about other classes of boot drives, settle time is
    # only needed for things that are hot-pluggable
    *)
	echo "boot device does not appear to need settle time"
	;;
esac

if [ "x$SYSFSPATH" != "x" ] ; then
	if [ ! -e $SYSFSPATH ] ; then
		# go and do the settle loop. continue after a while though, because
		# infinite loops really suck, and this early in the boot process,
		# the user can't use ctrl-c to break out.
		echo -n "waiting for boot device to appear... "
		N=0
		PRESENT=0
		while [ $PRESENT != 1 ] ; do
			case $(($N % 4)) in
			    0) echo -n '|' ;;
			    1) echo -n '/' ;;
			    2) echo -n '-' ;;
			    3) echo -n '\' ;;
			esac
			echo -n ""
			if [ -e $SYSFSPATH ] ; then
				PRESENT=1
				echo -e "\nboot device found, proceeding with boot"
			else
				if [ $N -ge 60 ] ; then
					echo -e "\nwaited kind of a long time; continuing and hoping for the best"
					PRESENT=1
				else
					sleep 0.25s
				fi
			fi
			N=$(($N + 1))
		done
	else
		# if by some chance it's already there, just roll on ahead
		echo "boot device is already present, proceeding with boot"
	fi
fi

umount -n sys
umount -n proc
