public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* sample /sbin/hotplug script
@ 2000-11-28  0:09 David Brownell
  0 siblings, 0 replies; only message in thread
From: David Brownell @ 2000-11-28  0:09 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1100 bytes --]

I've been quite pleased to see many recent improvement in hotplug
support ... 2.4 test11 is now hotplugging USB network adapters for
me when I need that!  And many other devices at least get their
driver modules autoloaded (using modutils support) even if some
devices still need hand setup before they're usable.

To make a long story short, if you'd like to try it out I strongly
encourage you to put the attached shell script into /sbin/hotplug
and see if it does the right things when you plug in USB devices
or Cardbus adapters into your system.  (And configure HOTPLUG;
see its config.help for more information.)

The test11 release has hotplug support for PCI, USB, and also for
networking interfaces.  (Though I've not tested the Cardbus/PCI
support myself, and know it can't support class-based driver binding
without a small patch I'll resubmit.)

This script should handle all three.  It's a start!  Improvements
should be easy enough ... :-)

- Dave

p.s. I'm particularly aware of contributions to the Linux
    hotplugging effort from Keith Owens, Adam Richter, and
    Jeff Garzik.




[-- Attachment #2: hotplug.sh --]
[-- Type: application/octet-stream, Size: 11904 bytes --]

#!/bin/bash
#
# This is a reference implementation for /sbin/hotplug, which works
# on most GNU/Linux systems without ANY additional software.
#
# This implementation delegates to type-specific agents, such as
# "/etc/hotplug/network.agent", where they exist.  Otherwise it
# uses built-in in support for USB and PCI hotplug events, trying
# to load a module that appears to handle this device.
#
# /proc/sys/kernel/hotplug controls the program invoked by the kernel.
# /sbin/hotplug is the default value, which you may change.  (A null
# string prevents the kernel from invoking a hotplug program.)  To make
# this functionality available, your kernel config must include HOTPLUG
# (and currently KMOD).
#
#
# HISTORY:
#
# 24-Nov-2000 Update matching a test12 prepatch
# 21-Nov-2000 Update for 2.4.0-test11 "net", "pci" support (needs
#	another kernel fix for pci/cardbus)
# 06-Nov-2000 Build in support for modules.{usb,pci}map; Cardbus may
#	now work, with a kernel patch.  /etc/hotplug directory hooks.
# 09-Jul-2000 Initial version; kernel USB hotplugging starts, using
#	the existing USB scripts
#

# DEBUG=yes export DEBUG
PATH=/bin:/sbin:/usr/sbin:/usr/bin

if [ -t -o ! -x /usr/bin/logger ]; then
    mesg () {
	echo "$@"
    }
else
    mesg () {
	/usr/bin/logger -t $0 "$@"
    }
fi

usage ()
{
    # Only one parameter (event type) is mandatory.
    # Everything else is type-specific.

    if [ -t ]; then
	echo "Usage: $0 {usb,pci,net,...} ..."
	echo "Environment parameters are also type-specific."
# FIXME: list non-builtin agents (/etc/hotplug/*.agent)
    else
	mesg "illegal usage $*"
    fi
    exit 1
}

if [ "$DEBUG" != "" ]; then
    mesg "arguments ($*) env (`env`)"
fi


# Only one required argument:  event type type being dispatched.
# Examples:  usb, pci, isapnp, net, ieee1394, printer, disk, ... 
if [ $# -lt 1 ]; then
    usage
elif [ $1 = help -o $1 = '--help' ]; then
    usage

# Prefer to delegate event handling:
#   /sbin/hotplug FOO ..args.. ==> /etc/hotplug/FOO.agent ..args..
#
elif [ -x /etc/hotplug/$1.agent ]; then
    shift
    exec /etc/hotplug/$1.agent "$@"
fi


####################################################################
#
# Builtin agent code -- "80% solution" fallbacks.
#
# For USB and PCI, this directly processes modutils (2.3.20+)
# output for MODULE_DEVICE_TABLE entries, so that you can hotplug
# after installing only this script.
#
# For network interfaces, if you've got "ifup" it'll try to
# bring the interface up.  Otherwise, update for your distro.
#

#
# This module loading uses BASH ("declare -i") and some version of
# AWK, typically /bin/gawk.  Most GNU/Linux distros have these,
# but some specialized ones (floppy based, etc) may not.
#
# NOTE:  The match algorithms here aren't any smarter than those
# in the kernel; they take the first match available, even if
# it's not the "best" (most specific) match.  That's not really
# a feature.  Agents written in other language can more easily
# support sophisticated driver selection algorithms, prioritize
# (or add) administrator-specified bindings, and so on.
#
# FIXME: can we avoid using '<<' to parse composite variables?
# That may require a writable /tmp; do we fail cleanly in
# that phase of system bootstrapping?

AWK=gawk
MODDIR=/lib/modules/`uname -r`

# ISAPNP_MAP=$MODDIR/modules.isapnpmap
PCI_MAP=$MODDIR/modules.pcimap
USB_MAP=$MODDIR/modules.usbmap


####################################################################
#
# Kernel USB params are:
#	
#	ACTION=%s [add or remove]
#	PRODUCT=%x/%x/%x
#	INTERFACE=%d/%d/%d
#	TYPE=%d/%d/%d
#
# And if usbdevfs is configured, also:
#
#	DEVFS=/proc/bus/usb
#	DEVICE=/proc/bus/usb/%03d/%03d
#
# If usbdevfs is mounted on /proc/bus/usb, $DEVICE is a file which
# can be read to get the device's current configuration descriptor.
#
declare -i usb_idVendor usb_idProduct usb_bcdDevice
declare -i usb_bDeviceClass usb_bDeviceSubClass usb_bDeviceProtocol
declare -i usb_bInterfaceClass usb_bInterfaceSubClass usb_bInterfaceProtocol

usb_convert_vars ()
{
    local XPROD
    XPROD=`echo $PRODUCT | $AWK -F/ '{print "0x" $1, "0x" $2, "0x" $3 }'`
    read usb_idVendor usb_idProduct usb_bcdDevice << EOT
$XPROD
EOT

    if [ x$TYPE != x ]; then
    IFS=/ read usb_bDeviceClass usb_bDeviceSubClass usb_bDeviceProtocol << EOT
$TYPE
EOT
    else
	# out-of-range values
	usb_bDeviceClass=1000
	usb_bDeviceSubClass=1000
	usb_bDeviceProtocol=1000
    fi

    if [ x$INTERFACE != x ]; then
    IFS=/ read usb_bInterfaceClass usb_bInterfaceSubClass usb_bInterfaceProtocol << EOT
$INTERFACE
EOT
    else
	# out-of-range values
	usb_bInterfaceClass=1000
	usb_bInterfaceSubClass=1000
	usb_bInterfaceProtocol=1000
    fi
}

declare -i USB_ANY
USB_ANY=0

# stdin is "modules.usbmap" syntax
usb_map_modules ()
{
    # convert the usb_device_id fields to integers as we read them 
    local module ignored
    declare -i idVendor idProduct bcdDevice_lo bcdDevice_hi
    declare -i bDeviceClass bDeviceSubClass bDeviceProtocol
    declare -i bInterfaceClass bInterfaceSubClass bInterfaceProtocol

    # comment line lists (current) usb_device_id field names
    read ignored

    # look at each usb_device_id entry
    while read module idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol ignored
    do
	: checkmatch $module

	: idVendor $idVendor $usb_idVendor
	if [ $idVendor -ne $USB_ANY -a $idVendor -ne $usb_idVendor ]; then
	    continue
	fi
	# NOTE: zero product code isn't the wildcard! 
	: idProduct $idProduct $usb_idProduct
	if [ $idVendor -ne $USB_ANY -a $idProduct -ne $usb_idProduct ]; then
	    continue
	fi

	: bcdDevice range $bcdDevice_hi $bcdDevice_lo actual $usb_bcdDevice
	if [ $bcdDevice_lo -ge $usb_bcdDevice ]; then
	    continue
	fi
	if [ $bcdDevice_hi -ne $USB_ANY -a $bcdDevice_hi -ge $usb_bcdDevice ]; then
	    continue
	fi

	: bDeviceClass $bDeviceClass $usb_bDeviceClass
	if [ $bDeviceClass -ne $USB_ANY -a $bDeviceClass -ne $usb_bDeviceClass ]; then
	    continue
	fi
	: bDeviceSubClass $bDeviceSubClass $usb_bDeviceSubClass
	if [ $bDeviceSubClass -ne $USB_ANY -a $bDeviceSubClass -ne $usb_bDeviceSubClass ]; then
	    continue
	fi
	: bDeviceProtocol $bDeviceProtocol $usb_bDeviceProtocol
	if [ $bDeviceProtocol -ne $USB_ANY -a $bDeviceProtocol -ne $usb_bDeviceProtocol ]; then
	    continue
	fi

	# NOTE:  for now, this only checks the first of perhaps
	# several interfaces for this device.

	: bInterfaceClass $bInterfaceClass $usb_bInterfaceClass
	if [ $bInterfaceClass -ne $USB_ANY -a $bInterfaceClass -ne $usb_bInterfaceClass ]; then
	    continue
	fi
	: bInterfaceSubClass $bInterfaceSubClass $usb_bInterfaceSubClass
	if [ $bInterfaceSubClass -ne $USB_ANY -a $bInterfaceSubClass -ne $usb_bInterfaceSubClass ]; then
	    continue
	fi
	: bInterfaceProtocol $bInterfaceProtocol $usb_bInterfaceProtocol
	if [ $bInterfaceProtocol -ne $USB_ANY -a $bInterfaceProtocol -ne $usb_bInterfaceProtocol ]; then
	    continue
	fi

	# It was a match!
	DRIVER=$module
	: driver $DRIVER
	break;
    done
}


####################################################################
#
# Kernel Cardbus/PCI params are (current patch):
#	
#	PCI_CLASS=%X
#	PCI_ID=%X/%X
#	PCI_SLOT_NAME=%s
#	PCI_SUBSYS_ID=%X/%X
#
# If /proc is mounted, /proc/bus/pci/$PCI_SLOT_NAME is almost the name
# of the binary device descriptor file ... just change ':' to '/'.
#
declare -i pci_class
declare -i pci_id_vendor pci_id_device
declare -i pci_subid_vendor pci_subid_device

pci_convert_vars ()
{
    XID=`echo $PCI_ID | $AWK -F/ '{print "0x" $1, "0x" $2 }'`
    read pci_class pci_id_vendor pci_id_device << EOT
0x$PCI_CLASS $XID
EOT

    XID=`echo $PCI_SUBSYS_ID | $AWK -F/ '{print "0x" $1, "0x" $2 }'`
    read pci_subid_vendor pci_subid_device << EOT
$XID
EOT
}

declare -i PCI_ANY
PCI_ANY=0xffffffff

# stdin is "modules.pcimap" syntax
pci_map_modules ()
{
    # convert the usb_device_id fields to integers as we read them 
    local module ignored
    declare -i vendor device
    declare -i subvendor subdevice
    declare -i class class_mask
    declare -i class_temp

    # comment line lists (current) pci_device_id field names
    read ignored

    # look at each pci_device_id entry
    while read module vendor device subvendor subdevice class class_mask ignored
    do
	: checkmatch $module

	: vendor $vendor $pci_id_vendor
	if [ $vendor -ne $PCI_ANY -a $vendor -ne $pci_id_vendor ]; then
	    continue
	fi
	: device $device $pci_id_device
	if [ $device -ne $PCI_ANY -a $device -ne $pci_id_device ]; then
	    continue
	fi
	: sub-vendor $subvendor $pci_subid_vendor
	if [ $subvendor -ne $PCI_ANY -a $subvendor -ne $pci_subid_vendor ]; then
	    continue
	fi
	: sub-device $subdevice $pci_subid_device
	if [ $subdevice -ne $PCI_ANY -a $subdevice -ne $pci_subid_device ]; then
	    continue
	fi

	class_temp="$pci_class & $class_mask"
	if [ $class_temp -eq $class ]; then
	    DRIVER=$module
	    : driver $DRIVER
	    break;
	fi
    done
}

####################################################################
#
# usage: load_driver type filename description
#
# (always) modprobes a single driver module, and optionally
# invokes a module-specific setup script.
#
load_driver ()
{
    # find the driver using modutils output
    if [ -f $2 ]; then
	$1_convert_vars
	$1_map_modules < $2
    else
	mesg "$2 missing"
	exit 1
    fi
    if [ x$DRIVER = x ]; then
	mesg "... no driver for $3"
	exit 2
    fi

    if [ "$DEBUG" != "" ]; then
	mesg Module $DRIVER matches $3
    fi

    # maybe the driver needs loading
    if ! lsmod | grep "^$DRIVER "
    then
	if ! modprobe $DRIVER
	then
	    mesg "... can't load module $DRIVER"
	    exit 3
	fi
    fi

    # NOTE:  this assumes that driver bound to
    # this device; it'd be better to check ...

    # run any setup script; no /etc/modules.conf changes needed
    if [ -x /etc/hotplug/$1/$DRIVER ]; then
	if [ "$DEBUG" != "" ]; then
	    mesg Driver setup: $DRIVER
	fi
	exec /etc/hotplug/$1/$DRIVER
    else
	exit 0
    fi
}

####################################################################
#
# Two basic policy agents are now built in:  they try to load the
# USB or PCI driver module corresponding to newly added devices.
# They're used as backup, in case no smarter tools are available.
#
if [ $1 = usb ]; then
    # Until all kernel USB drivers get updated, you need:
    # http://www.linux-usb.org/policy.html

    if [ -f /etc/usb/policy ]; then
	exec /etc/usb/policy
    fi

    if [ x$PRODUCT = x ]; then
	mesg Bad USB invocation
	exit 1
    fi

    # no more commandline params
    case x$ACTION in
    xadd)
	load_driver usb $USB_MAP "USB product $PRODUCT"
	# won't return
	;;
    xremove)
	: USB remove, ignored
	exit 0;;
    x*|xhelp)
	usage $* ;;
    esac

elif [ $1 = pci ]; then
    # NOTE: PCI includes CardBus

    if [ x$PCI_CLASS = x ]; then
	mesg Bad PCI invocation
	exit 1
    fi

    case x$ACTION in
    xadd)
	if [ "$DEBUG" != "" -a -x /sbin/lspci ]; then
	    mesg New PCI Device: `/sbin/lspci -s $PCI_SLOT_NAME`
	fi
	load_driver pci $PCI_MAP "PCI device at slot $PCI_SLOT_NAME"
	# won't return
	;;
    xremove)
	: PCI remove, ignored
	exit 0;;
    *|help)
	usage $* ;;
    esac

elif [ $1 = net ]; then
    if [ x$INTERFACE = x ]; then
	mesg Bad NET invocation
	exit 1
    fi

    if [ x$ACTION = xregister -a -x /sbin/ifup ]; then
	/sbin/ifup $INTERFACE &
	exit 0
    fi
fi

mesg "$0: event type '$1' unsupported"
exit 1

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2000-11-28  8:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2000-11-28  0:09 sample /sbin/hotplug script David Brownell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox