#!/bin/bash # # USB-specific hotplug policy agent. # # This should handle 2.2.18+ and 2.4.* USB hotplugging, # with a consistent framework for adding device and driver # specific treatments. # # Kernel USB hotplug params include: # # ACTION=%s [add or remove] # PRODUCT=%x/%x/%x # INTERFACE=%d/%d/%d # TYPE=%d/%d/%d # # And if usbfs (originally called usbdevfs) is configured, also: # # DEVFS=/proc/bus/usb # DEVICE=/proc/bus/usb/%03d/%03d # # This script then adds the variable: # # REMOVER=/var/run/usb/ # # This is the path where the script would like to find a remover, if # the target device needs one. This script is executed on remove if # it is executable when the remove happens. # # If usbfs is mounted on /proc/bus/usb, $DEVICE is a file which # can be read to get the device's current configuration descriptor. # (The "usbmodules" utility helps do that.) # # On systems using Linux 2.4.* kernels, be sure to use the right # modutils (2.4.2+). That ensures that hotplugging uses the list # of modules installed for your kernel, rather than the one that's # included here for use on systems without MODULE_DEVICE_TABLE # support. # # # HISTORY: # # 18-Jan-2002 fix match algorithm in usb_map_modules() # 14-Jan-2002 fix work around 2.2 brokeness of $PRODUCT # 09-Jan-2002 REMOVER for system without usbdevfs # 14-Mar-2001 Cleanup, bitmask the match_flags # 26-Feb-2001 Cleanup, support comments (Gioele Barabucci) # 23-Jan-2001 Update 2.2 handling; unfortunately there's no "feature # test" that can work robustly # 05-Jan-2001 Quick hack for kernel 2.4.0 and modutils 2.4.1 # 03-Jan-2001 Initial version of "new" hotplug agent, using feedback # and contributions from Adam Richter, Ryan VanderBijl, # Norbert Preining, Florian Lohoff, David Brownell and # others. To replace the original /etc/usb/policy. (db) # 15-Feb-2001 Remove use of "<<" (Adam Richter) # # $Id: usb.agent,v 1.1 2004/06/23 11:31:47 namikosi Exp $ # if [ -f /etc/sysconfig/usb ]; then . /etc/sysconfig/usb fi cd /etc/hotplug . hotplug.functions # DEBUG=yes export DEBUG # generated by modutils, for current 2.4.x kernels MAP_CURRENT=$MODULE_DIR/modules.usbmap # used if MAP_CURRENT is missing; for 2.2.x kernels MAP_DISTMAP=$HOTPLUG_DIR/usb.distmap # # used for kernel drivers that don't show up in CURRENT or DISTMAP, # currently input drivers (joysticks, UPSs, etc) and usb storage; # MAP_HANDMAP=$HOTPLUG_DIR/usb.handmap # # used to run config scripts for user mode drivers (jPhoto, gPhoto2, # rio500 tools, etc) ... instead of naming kernel modules, it names # config scripts (which mustn't overlap with kernel modules). Those # could change $DEVICE permissions, etc. # MAP_USERMAP=$HOTPLUG_DIR/usb.usermap # accumulates list of modules we may care about DRIVERS="" if [ "$PRODUCT" = "" -o "$ACTION" = "" ]; then mesg Bad USB agent invocation exit 1 fi # we can't "unset IFS" on bash1, so save a copy DEFAULT_IFS="$IFS" # # Each modules.usbmap format line corresponds to one entry in a # MODULE_DEVICE_TABLE(usb,...) declaration in a kernel file. # # Think of it as a database column with up to three "match specs" # to associate kernel modules with particular devices or classes # of device. The match specs provide a reasonably good filtering # mechanism, but some driver probe() routines need to provide # extra filtering. # 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 () { if [ "$AWK" = "" ]; then mesg "can't find awk!" exit 1 fi # work around 2.2 brokenness # munges the usb_bcdDevice such that it is a integer rather # than a float: e.g. 1.0 become 0100 PRODUCT=`echo $PRODUCT | sed -e "s+\.\([0-9]\)$+.\10+" -e "s/\.$/00/" \ -e "s+/\([0-9]\)\.\([0-9][0-9]\)+/0\1\2+" \ -e "s+/\([0-9][0-9]\)\.\([0-9][0-9]\)+/\1\2+"` set `echo $PRODUCT | $AWK -F/ '{print "0x" $1, "0x" $2, "0x" $3 }'` '' usb_idVendor=$1 usb_idProduct=$2 usb_bcdDevice=$3 if [ "$TYPE" != "" ]; then IFS=/ set $TYPE '' usb_bDeviceClass=$1 usb_bDeviceSubClass=$2 usb_bDeviceProtocol=$3 IFS="$DEFAULT_IFS" else # out-of-range values usb_bDeviceClass=1000 usb_bDeviceSubClass=1000 usb_bDeviceProtocol=1000 fi if [ "$INTERFACE" != "" ]; then IFS=/ set $INTERFACE '' usb_bInterfaceClass=$1 usb_bInterfaceSubClass=$2 usb_bInterfaceProtocol=$3 IFS="$DEFAULT_IFS" else # out-of-range values usb_bInterfaceClass=1000 usb_bInterfaceSubClass=1000 usb_bInterfaceProtocol=1000 fi } declare -i USB_MATCH_VENDOR=0x0001 declare -i USB_MATCH_PRODUCT=0x0002 declare -i USB_MATCH_DEV_LO=0x0004 declare -i USB_MATCH_DEV_HI=0x0008 declare -i USB_MATCH_DEV_CLASS=0x0010 declare -i USB_MATCH_DEV_SUBCLASS=0x0020 declare -i USB_MATCH_DEV_PROTOCOL=0x0040 declare -i USB_MATCH_INT_CLASS=0x0080 declare -i USB_MATCH_INT_SUBCLASS=0x0100 declare -i USB_MATCH_INT_PROTOCOL=0x0200 # # stdin is "modules.usbmap" syntax # on return, all matching modules were added to $DRIVERS # usb_map_modules () { # convert the usb_device_id fields to integers as we read them local line module declare -i match_flags declare -i idVendor idProduct bcdDevice_lo bcdDevice_hi declare -i bDeviceClass bDeviceSubClass bDeviceProtocol declare -i bInterfaceClass bInterfaceSubClass bInterfaceProtocol # look at each usb_device_id entry # collect all matches in $DRIVERS while read line do # comments are lines that start with "#" ... # be careful, they still get parsed by bash! case "$line" in \#*) continue ;; esac set $line module=$1 match_flags=$2 idVendor=$3 idProduct=$4 bcdDevice_lo=$5 bcdDevice_hi=$6 bDeviceClass=$7 bDeviceSubClass=$8 bDeviceProtocol=$9 shift 9 bInterfaceClass=$1 bInterfaceSubClass=$2 bInterfaceProtocol=$3 : checkmatch $module : idVendor $idVendor $usb_idVendor if [ $USB_MATCH_VENDOR -eq $(( $match_flags & $USB_MATCH_VENDOR )) ] && [ $idVendor -ne $usb_idVendor ]; then continue fi : idProduct $idProduct $usb_idProduct if [ $USB_MATCH_PRODUCT -eq $(( $match_flags & $USB_MATCH_PRODUCT )) ] && [ $idProduct -ne $usb_idProduct ]; then continue fi : bcdDevice range $bcdDevice_hi $bcdDevice_lo actual $usb_bcdDevice if [ $USB_MATCH_DEV_LO -eq $(( $match_flags & $USB_MATCH_DEV_LO )) ] && [ $usb_bcdDevice -lt $bcdDevice_lo ]; then continue fi # bcdDevice_lo <= bcdDevice <= bcdDevice_hi if [ $USB_MATCH_DEV_HI -eq $(( $match_flags & $USB_MATCH_DEV_HI )) ] && [ $usb_bcdDevice -gt $bcdDevice_hi ]; then continue fi : bDeviceClass $bDeviceClass $usb_bDeviceClass if [ $USB_MATCH_DEV_CLASS -eq $(( $match_flags & $USB_MATCH_DEV_CLASS )) ] && [ $bDeviceClass -ne $usb_bDeviceClass ]; then continue fi : bDeviceSubClass $bDeviceSubClass $usb_bDeviceSubClass if [ $USB_MATCH_DEV_SUBCLASS -eq $(( $match_flags & $USB_MATCH_DEV_SUBCLASS )) ] && [ $bDeviceSubClass -ne $usb_bDeviceSubClass ]; then continue fi : bDeviceProtocol $bDeviceProtocol $usb_bDeviceProtocol if [ $USB_MATCH_DEV_PROTOCOL -eq $(( $match_flags & $USB_MATCH_DEV_PROTOCOL )) ] && [ $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 [ $USB_MATCH_INT_CLASS -eq $(( $match_flags & $USB_MATCH_INT_CLASS )) ] && [ $bInterfaceClass -ne $usb_bInterfaceClass ]; then continue fi : bInterfaceSubClass $bInterfaceSubClass $usb_bInterfaceSubClass if [ $USB_MATCH_INT_SUBCLASS -eq $(( $match_flags & $USB_MATCH_INT_SUBCLASS )) ] && [ $bInterfaceSubClass -ne $usb_bInterfaceSubClass ]; then continue fi : bInterfaceProtocol $bInterfaceProtocol $usb_bInterfaceProtocol if [ $USB_MATCH_INT_PROTOCOL -eq $(( $match_flags & $USB_MATCH_INT_PROTOCOL )) ] && [ $bInterfaceProtocol -ne $usb_bInterfaceProtocol ]; then continue fi # It was a match! DRIVERS="$module $DRIVERS" : drivers $DRIVERS done } # # declare a REMOVER name that the add action can use to create a # remover, or that the remove action can use to execute a remover. # if [ "$DEVICE" = "" ]; then declare -rx REMOVER=/var/run/usb/`echo "$INTERFACE/$PRODUCT/$TYPE" | sed -e 's;/;%;g'` else declare -rx REMOVER=/var/run/usb/`echo $DEVICE | sed -e 's;/;%;g'` fi # # What to do with this USB hotplug event? # case $ACTION in add) # Let the usb subsystem finish talking to the device, before we do # NOTE: This hack doesn't fix the real problem. It seems to be # needed for some HID devices because "usbmodules" does control # messaging, and the HCDs don't all queue such messages safely. # sleep 3 usb_convert_vars FOUND=false LABEL="USB product $PRODUCT" if [ -e "$REMOVER" ]; then rm -f "$REMOVER" fi # on 2.4 systems, modutils 2.4.2+ maintains MAP_CURRENT # ... otherwise we can't rely on it (sigh) case "$KERNEL" in 2.4.*|2.5.*) if [ -r $MAP_CURRENT ]; then load_drivers usb $MAP_CURRENT "$LABEL" fi;; *) if [ -r $MAP_DISTMAP ]; then load_drivers usb $MAP_DISTMAP "$LABEL" fi;; esac if [ "$DRIVERS" != "" ]; then FOUND=true fi # cope with special driver module configurations # (mostly HID devices, until input can hotplug) if [ -r $MAP_HANDMAP ]; then load_drivers usb $MAP_HANDMAP "$LABEL" if [ "$DRIVERS" != "" ]; then FOUND=true fi fi # some devices have user-mode drivers (no kernel module, but config) # or specialized user-mode setup helpers if [ -r $MAP_USERMAP ]; then MODPROBE=: load_drivers usb $MAP_USERMAP "$LABEL" if [ "$DRIVERS" != "" ]; then FOUND=true fi fi if [ "$FOUND" = "false" ]; then mesg "... no modules for $LABEL" exit 2 fi if [ -x /sbin/devlabel ]; then /sbin/devlabel restart fi ;; remove) if [ -x $REMOVER ]; then $REMOVER fi rm -f $REMOVER # This doesn't happen often enough to make it a performance problem... [ -x /usr/sbin/updfstab ] && /usr/sbin/updfstab if [ -x /sbin/devlabel ]; then /sbin/devlabel restart fi ;; *) debug_mesg USB $ACTION event not supported exit 1 ;; esac