#!/bin/sh
#
# input-specific hotplug policy agent.
#
# This should handle 2.6.* input hotplugging,
# with a consistent framework for adding device and driver
# specific handling.
#
# Normally, adding a input device will modprobe handler(s) for
# this device.
#
# Kernel input hotplug params include (not all of them may be available):
#	
#	ACTION=%s [add or remove]
#	PRODUCT=%x/%x/%x/%x
#	NAME=%s
#	PHYS=%s
#	EV=%lx
#	KEY=%lx %lx ...
#	REL=%lx
#	ABS=%lx %lx ...
#	MSC=%lx
#	LED=%lx
#	SND=%lx
#	FF=%lx %lx ...
#
# HISTORY:
#
# 30-Jul-2003	initial version
#

cd /etc/hotplug
. ./hotplug.functions
# DEBUG=yes export DEBUG

# generated by module-init-tools
MAP_CURRENT=$MODULE_DIR/modules.inputmap

# accumulates list of modules we may care about
DRIVERS=""

if [ "$ACTION" = "" ]; then
    mesg Bad INPUT agent invocation, no action
    exit 1
fi

# we can't "unset IFS" on bash1, so save a copy
DEFAULT_IFS="$IFS"

#
# Each modules.inputmap format line corresponds to one entry in a
# MODULE_DEVICE_TABLE(input,...) declaration in a kernel file.
#
matchBits=0; i_bustype=0; i_vendor=0; i_product=0; i_version=0; i_evBits=0

input_join_words ()
{
    name="$1"
    array="$2"

    if [ "$array" = '' ]; then
	return
    fi

    set $array

    tmp="$1"
    shift
    while [ "$#" -gt 0 ]; do
	tmp="$tmp:$1"
	shift
    done

    eval "$name=\"$tmp\""
}

input_convert_vars ()
{
    if [ "$PRODUCT" != "" ]; then
	IFS=/
	set $PRODUCT ''
	IFS="$DEFAULT_IFS"
	i_bustype=$((0x$1))
	i_vendor=$((0x$2))
	i_product=$((0x$3))
	i_version=$((0x$4))
    fi

    if [ "$EV" != "" ]; then
	i_evBits=$((0x$EV))
    fi

    input_join_words i_keyBits "$KEY"
    input_join_words i_relBits "$REL"
    input_join_words i_absBits "$ABS"
    input_join_words i_mscBits "$MSC"
    input_join_words i_ledBits "$LED"
    input_join_words i_sndBits "$SND"
    input_join_words i_ffBits  "$FF"
}

INPUT_DEVICE_ID_MATCH_BUS=1
INPUT_DEVICE_ID_MATCH_VENDOR=2
INPUT_DEVICE_ID_MATCH_PRODUCT=4
INPUT_DEVICE_ID_MATCH_VERSION=8
INPUT_DEVICE_ID_MATCH_EVBIT=$((0x010))
INPUT_DEVICE_ID_MATCH_KEYBIT=$((0x020))
INPUT_DEVICE_ID_MATCH_RELBIT=$((0x040))
INPUT_DEVICE_ID_MATCH_ABSBIT=$((0x080))
INPUT_DEVICE_ID_MATCH_MSCBIT=$((0x100))
INPUT_DEVICE_ID_MATCH_LEDBIT=$((0x200))
INPUT_DEVICE_ID_MATCH_SNDBIT=$((0x400))
INPUT_DEVICE_ID_MATCH_FFBIT=$((0x800))


input_match_bits ()
{
    mod_bits=$1
    dev_bits=$2

    if [ "$dev_bits" = "" ]; then
	return 0
    fi
    mword=$((0x${mod_bits##*:}))
    dword=$((0x${dev_bits##*:}))

    while true; do
	if [ $(( $mword & $dword != $mword )) -eq 1 ]; then
	    return 1
	fi

	mod_bits=${mod_bits%:*}
	dev_bits=${dev_bits%:*}

	case "$mod_bits-$dev_bits" in
	    *:*-*:* )
		: continue
	    ;;
	    *:*-*|*-*:* )
		return 0
	    ;;
	    * )
		return 1
	    ;;
	esac
    done
}

#
# stdin is "modules.inputmap" syntax
# on return, all matching modules were added to $DRIVERS
#
input_map_modules ()
{
    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"
	matchBits=$(($2))

	bustype=$(($3))
	vendor=$(($4))
	product=$(($5))
	version=$(($6))

	evBits="$7"
	keyBits="$8"
	relBits="$9"

	shift 9
	absBits="$1"
	cbsBits="$2"
	ledBits="$3"
	sndBits="$4"
	ffBits="$5"
	driverInfo=$(($6))

	: checkmatch $module

	: bustype $bustype $i_bustype
        if [ $INPUT_DEVICE_ID_MATCH_BUS -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_BUS )) ] && 
	   [ $bustype -ne $i_bustype ]; then
	    continue
	fi

	: vendor $vendor $i_vendor
        if [ $INPUT_DEVICE_ID_MATCH_VENDOR -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_VENDOR )) ] && 
	   [ $vendor -ne $i_vendor ]; then
	    continue
	fi

	: product $product $i_product
        if [ $INPUT_DEVICE_ID_MATCH_PRODUCT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_PRODUCT )) ] && 
	   [ $product -ne $i_product ]; then
	    continue
	fi

	# version i_version $i_version < version $version
        if [ $INPUT_DEVICE_ID_MATCH_VERSION -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_VERSION )) ] && 
	   [ $version -ge $i_version ]; then
	    continue
	fi

	: evBits $evBits $i_evBits
        if [ $INPUT_DEVICE_ID_MATCH_EVBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_EVBIT )) ] && 
	   input_match_bits "$evBits" "$i_evBits"; then
	    continue
	fi
	: keyBits $keyBits $i_keyBits
        if [ $INPUT_DEVICE_ID_MATCH_KEYBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_KEYBIT )) ] && 
	   input_match_bits "$keyBits" "$i_keyBits"; then
	    continue
	fi
	: relBits $relBits $i_relBits
        if [ $INPUT_DEVICE_ID_MATCH_RELBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_RELBIT )) ] && 
	   input_match_bits "$relBits" "$i_relBits"; then
	    continue
	fi

	: absBits $absBits $i_absBits
        if [ $INPUT_DEVICE_ID_MATCH_ABSBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_ABSBIT )) ] && 
	   input_match_bits "$absBits" "$i_absBits"; then
	    continue
	fi

	: mscBits $mscBits $i_mscBits
        if [ $INPUT_DEVICE_ID_MATCH_MSCBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_MSCBIT )) ] && 
	   input_match_bits "$mscBits" "$i_mscBits"; then
	    continue
	fi

	: ledBits $ledBits $_ledBits
        if [ $INPUT_DEVICE_ID_MATCH_LEDBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_LEDBIT )) ] && 
	   input_match_bits "$ledBits" "$i_ledBits"; then
	    continue
	fi

	: sndBits $sndBits $i_sndBits
        if [ $INPUT_DEVICE_ID_MATCH_SNDBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_SNDBIT )) ] && 
	   input_match_bits "$sndBits" "$i_sndBits"; then
	    continue
	fi

	: ffBits $ffBits $i_ffBits
        if [ $INPUT_DEVICE_ID_MATCH_FFBIT -eq $(( $matchBits & $INPUT_DEVICE_ID_MATCH_FFBIT )) ] && 
	   input_match_bits "$ffBits" "$i_ffBits"; then
	    continue
	fi

	: driverInfo $driverInfo
	if [ $matchBits -eq 0 ] && [ $driverInfo -eq 0 ]; then
		continue
	fi

	# It was a match!
	case " $DRIVERS " in
	    *" $module "* )
		: already found
	    ;;
	    * )
		DRIVERS="$module $DRIVERS"
	    ;;
	esac
	: drivers $DRIVERS
    done
}

#
# What to do with this INPUT hotplug event?
#
case $ACTION in

add)

    input_convert_vars

    FOUND=false
    LABEL="INPUT product $PRODUCT"

    if [ -r $MAP_CURRENT ]; then
	load_drivers input $MAP_CURRENT "$LABEL"
    fi

    if [ "$DRIVERS" != "" ]; then
	FOUND=true
    fi

    if [ "$FOUND" = "false" ]; then
	debug_mesg "... no modules for $LABEL"
	exit 2
    fi

    ;;

remove)
    : nothing so far

    ;;

*)
    debug_mesg INPUT $ACTION event not supported
    exit 1
    ;;

esac
