#!/bin/sh
#
# usb.rc	This brings the USB subsystem up and down safely.
#
# $Id: usb.rc,v 1.10 2001/12/29 19:52:26 dbrownell Exp $
#
# Best invoked via /etc/init.d/hotplug or equivalent, with
# writable /tmp, /usr mounted, and syslogging active.
#
# Bus management is basically unrelated to runlevel changes; it
# relates to physical topology, including possibly hotpluggable
# busses (USB, Cardbus) or controllers.  If a bus is physically
# present, it should normally be available.
# 
# USB-dependant systems (iMacs, Aero, and so on) should statically
# link USB keyboard support into the kernel (USB core, UHCI/OHCI,
# hid, input, keybdev; and likely mousedev) so the system console
# can't be removed by accident.


PATH=/sbin:/bin:/usr/sbin:/usr/bin

STATIC_MODULE_LIST=
# USBD_ENABLE=true
X11_USBMICE_HACK=false

# source function library
if [ -f /etc/init.d/functions ]; then
	. /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ]; then
	. /etc/rc.d/init.d/functions
fi


# override any of the defaults above?
if [ -f /etc/default/hotplug.usb ]; then
    . /etc/default/hotplug.usb
    # e.g. USBD_ENABLE=false but STATIC_MODULE_LIST has your devices
fi


MOUSE_MODULES="mousedev input"

# In its currently-recommended configuration, XFree86 3.3.6 always opens
# /dev/input/mice; so mousedev and input must be loaded before X11 starts.
if [ $X11_USBMICE_HACK = true ]; then
    STATIC_MODULE_LIST="$MOUSE_MODULES $STATIC_MODULE_LIST"
fi


usb_boot_events ()
{
	# synthesize hotplug events if we can 
	# we need (non-bash) programs to parse descriptors.
	LISTER=`type -p usbmodules`
	if [ "$LISTER" = "" -o ! -f /proc/bus/usb/devices ]; then
		echo $"** can't synthesize root hub events"
		return
	fi

	# make sure the usb agent will run
	ACTION=add
	PRODUCT=0/0/0
	export ACTION PRODUCT

	DEVFS=/proc/bus/usb
	DEVICE=
	export DEVFS DEVICE

	# these notifications will be handled by pcimodules
	for DEVICE in /proc/bus/usb/*/*
	do
		/etc/hotplug/usb.agent
	done
}


maybe_start_usb ()
{
    local COUNT SYNTHESIZE
    COUNT=0
    SYNTHESIZE=false

    # if USB is partially initted, synthesize hotplug events since
    # the kernel probably dropped them (missing root, unwritable /tmp)
    if [ -d /proc/bus/usb -a `ls /proc/bus/usb 2>&1 | wc -l` -gt 2 ]; then
	SYNTHESIZE=true
    fi

    # if distro hasn't already done part of this ... load core,
    # and mount usbdevfs before the first hotplug agent fires
    # (so it'll be available to the agents).
    modprobe -q usbcore >/dev/null 2>&1
    if [ -d /proc/bus/usb ]; then
	# if it's not mounted, try to mount it
	if [ ! -f /proc/bus/usb/devices ]; then
	    if grep -q "[	 ]/proc/bus/usb[	 ]" /etc/fstab ; then
		mount /proc/bus/usb
	    else
		mount -t usbdevfs usbdevfs /proc/bus/usb
	    fi
	fi
    fi

    # Load Host Controller Drivers (HCDs) ... this automatically handles
    # systems with _both_ UHCI and OHCI controllers, without relying on
    # /proc or tools (lspci -v|grep USB, etc) to do so.  If hotplugging
    # is enabled on this system, initting a root hub will cause hotplug
    # events to fire for every device on the tree at that root.

    # NOTE: this prefers "usb-uhci"; you may prefer "uhci".

    # FIXME: all this should be driven by PCI hotplugging, and have
    # the blacklist control which uhci driver gets used.

    modprobe -q ehci-hcd >/dev/null 2>&1
    modprobe -q usb-ohci >/dev/null 2>&1
    modprobe -q usb-uhci >/dev/null 2>&1 || modprobe -q uhci >/dev/null 2>&1
    # modprobe -q uhci >/dev/null 2>&1 || modprobe -q usb-uhci >/dev/null 2>&1

    if [ -d /proc/bus/usb ]; then
	# If we see there are no busses, we "failed" and
	# can report so even if we're partially nonmodular.
	COUNT=`ls /proc/bus/usb | wc -l`
	if [ $COUNT -le 2 ]; then
	    umount /proc/bus/usb
	    rmmod usbcore >/dev/null 2>&1
	    return 1
	fi

    # if USB is fully modular and yet can clean up,
    # we know init failed without needing usbdevfs
    elif rmmod usbcore >/dev/null 2>&1; then
	return 1
    fi

    # hotplug events didn't fire during booting;
    # cope with devices that enumerated earlier
    # and may not have been fully configured.
    if [ $SYNTHESIZE = true ]; then
    	usb_boot_events
    fi

    # Some modules are statically loaded, perhaps because they are
    # needed to activate filesystem device nodes.
    for MODULE in $STATIC_MODULE_LIST; do
	modprobe $MODULE
    done

    # we did everything we could ...
    return 0
}

maybe_stop_usb ()
{
    # call this multiple times if you had to take down components of the
    # USB subsystem by hand; it cleans up whatever can
    # be cleaned up, letting the system quiesce further.

    # disconnect all controllers we can, and kernel drivers
    # HCDs first, so most drivers reduce their use counts.
    rmmod usb-ohci usb-uhci uhci >/dev/null 2>&1
    rmmod ehci-hcd hcd >/dev/null 2>&1

    # user mode code may keep usbdevfs busy for a while yet ...

    # OK, usbcore won't actually be removed unless there happen to be
    # no USB drivers loaded, and usbdevfs isn't mounted.  let's force
    # removal of autocleanable modules before trying to rmmod usbcore
    rmmod -as

    # Now let's workaround the fact that some USB modules never increase
    # their module use counts, so that "rmmod -a" won't unload them.
    # (And we can't use "modprobe --autoclean" anyway.)

    rmmod joydev keybdev wacom wmforce >/dev/null 2>&1
    rmmod cpia_usb cpia dc2xx rio500 mdc800 scanner >/dev/null 2>&1
    rmmod acm audio ibmcam uss720 printer hid >/dev/null 2>&1
    rmmod microtek dsbr100 evdev pegasus plusb >/dev/null 2>&1
    rmmod usb-serial usb-storage net1080 usbnet kaweth >/dev/null 2>&1
    rmmod ov511 videodev belkin_sa digi_acceleport empeg ftdi_sio >/dev/null 2>&1
    rmmod keyspan_pda keyspan omninet visor whiteheat usbserial >/dev/null 2>&1
    rmmod bluetooth dabusb mct_u232 >/dev/null 2>&1
    rmmod usbmouse usbkbd >/dev/null 2>&1

    rmmod $STATIC_MODULE_LIST >/dev/null 2>&1

    # ok, hope that user mode drivers/managers closed their fds.
    umount /proc/bus/usb >/dev/null 2>&1

    rmmod usbcore >/dev/null 2>&1

    # we did everything we could ...
    return 0;
}

# See how we were called.
case "$1" in
  start)
	maybe_start_usb
        ;;
  stop)
	maybe_stop_usb
        ;;
  status)
	echo $"USB Status for kernel: " `uname -srm`
	echo ''

	if [ -f /proc/bus/usb/devices ]; then
	    COUNT=`ls /proc/bus/usb | wc -l`
	    if [ $COUNT -gt 2 ]; then
		COUNT=`expr $COUNT - 2`
		echo $"USB up; bus count is $COUNT"
		grep "^[TPSI]:" /proc/bus/usb/devices
	    else
		echo $"$USB-devfs partially up; no busses"
	    fi
	    echo ''

	    echo $"USB Drivers Loaded: "
	    cat /proc/bus/usb/drivers
	else
	    echo $"USB-devfs is unavailable. "
	    if [ -f /proc/modules ] && fgrep -q usbcore /proc/modules; then
		echo $"USB module is loaded. "
	    else
		echo $"USB may be statically linked. "
	    fi
	    echo $"If khubd is running, that shows USB is available."
	fi
	echo ''

	if [ -f /proc/sys/kernel/hotplug ]; then
	    echo $"khubd/hotplug thread: "
	else
	    echo $"khubd thread:"
	fi
	ps -l | head -1
	ps -Al | egrep 'khubd' | grep -v grep
	echo ''

	lsmod
	echo ''

	# /proc/devices too? "usb", "input", and others ...

	;;
  restart)
	# always invoke by absolute path, else PATH=$PATH:
	$0 stop && $0 start
	;;
  *)
        echo $"Usage: $0 {start|stop|status|restart}"
        exit 1
esac
