#!/bin/bash
# $Id$
#
# input-device 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 ...
#
: ${ACTION?Bad invocation: \$ACTION is not set}

cd /etc/hotplug
. ./hotplug.functions

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

# used for kernel drivers that don't show up in MAP_CURRENT
# or to overwrite default from MAP_CURRENT
#
MAP_HANDMAP=$HOTPLUG_DIR/input.handmap

# 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 ()
{
    local name=$1; shift
    local tmp=$1; shift
    while [ "$#" -gt 0 ]; do
        tmp="$tmp:$1"; shift
    done
    eval $name=\$tmp
}

input_convert_vars ()
{
    if [ "$PRODUCT" != "" ]; then
        set -- `IFS=/; echo $PRODUCT`
        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
    # for a in $KEY; do i_keyBits="${i_keyBits:+$i_keyBits:}${a}"; done
    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 ()
{
    local mod_bits=$1 dev_bits=$2

    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 ()
{
    local line module
    local relBits mscBits ledBits sndBits keyBits absBits ffBits

    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))
        vendor=$(($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 )) ] &&
           [ $evBits -ne $(( $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 )) ] &&
           [ $relBits -ne $(( $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 )) ] &&
           [ $mscBits -ne $(( $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 )) ] &&
           [ $sndBits -ne $(( $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 -a $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)

        # If hwup does initialize the device there is nothing more to do
        if [ -x /sbin/hwup ] && /sbin/hwup  ${1:+$1-}devpath-$DEVPATH -o hotplug; then
            : nix
        else

            input_convert_vars

            FOUND="" 
            LABEL="INPUT product $PRODUCT"

            # cope with special driver module configurations first. So we can
            # overwrite default settings in MAP_CURRENT
            if [ -r $MAP_HANDMAP ]; then
                load_drivers usb $MAP_HANDMAP "$LABEL"
                FOUND="$FOUND${DRIVERS:+ $DRIVERS}"
            fi

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

        fi

        ;;

    remove)

        if [ -x /sbin/hwdown ] && /sbin/hwdown  ${1:+$1-}devpath-$DEVPATH -o hotplug; then
            : nix
        fi

        ;;

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

esac
