vdevd/helpers/LINUX/input.sh
#!/bin/dash 

# set up /dev/input/by-id and /dev/input/by-path

set -u

. "$VDEV_HELPERS/subr.sh"

# set up input device permissions...
# $1    input class
# return 0 on success
# return 1 on error
setup_input_permissions() {

   local MODE _INPUT_CLASS

   _INPUT_CLASS="$1"
   
   # permissions...
   if [ "$_INPUT_CLASS" = "js" ]; then 

      # joysticks are world-readable
      VDEV_VAR_INPUT_MODE=0664
   fi

   vdev_permissions $VDEV_VAR_INPUT_OWNER:$VDEV_VAR_INPUT_GROUP $VDEV_VAR_INPUT_MODE "$VDEV_MOUNTPOINT/$VDEV_PATH"
   return $?
}


# store all device properties
# call this after stat_input and stat_path
# $1+   Names of variables to grab from the caller's context
store_properties() {
   
   # NOTE: pulling positional arguments from caller...
   vdev_add_properties "$VDEV_METADATA" $@

   # if this is an evdev keyboard, add X.org keyboard properties (i.e. from /etc/default/keyboard)
   if [ -n "$(echo "$VDEV_OS_DEVNAME" | /bin/egrep "event[0-9]+")" ] && [ -n "$VDEV_INPUT_KEY" ]; then
      
      # subshell, so we don't have to worry about messing up the environment 
      # NOTE: this is a bit of a hack, left over from the old hal days.
      # It would make more sense to me if X.org simply parsed this file, instead of 
      # having the device manager do so on its behalf.
      if [ -f "/etc/default/keyboard" ]; then (
         
         . "/etc/default/keyboard"

         XKBMODEL=""
         XKBLAYOUT=""
         XKBVARIANT=""
         KMAP=""
         ALL_PROPS="XKBMODEL XKBLAYOUT XKBVARIANT BACKSPACE KMAP"
      
         vdev_add_properties "$VDEV_METADATA" $ALL_PROPS
      ) fi
      
   fi

   return 0
}


# entry point 
# return 0 on success
# return 2 if input helper fails
main() {

   local SYSFS_PATH VDEV_INPUT_PROPERTIES STAT_RC VDEV_PATH_PROPERTIES VDEV_PROPERTIES VDEV_PERSISTENT_PATH VDEV_PERSISTENT_PATH_TAG INPUT_CLASS CLASSLESS_EVDEV BUS SERIAL IFNO BY_ID INPUT_DATA
      
   # full sysfs path
   SYSFS_PATH="$VDEV_OS_SYSFS_MOUNTPOINT/$VDEV_OS_DEVPATH"

   # if we don't know the device path, then there's not much we can do...
   if [ "$VDEV_PATH" = "UNKNOWN" ]; then 
      return 0
   fi

   # if removing, just blow away the symlinks
   if [ "$VDEV_ACTION" = "remove" ]; then 
      
      vdev_cleanup "$VDEV_METADATA"
      return 0
   fi


   # stat the device!
   VDEV_INPUT_PROPERTIES=""
   VDEV_INPUT_CLASS=""
   VDEV_INPUT_KEY=""
   
   INPUT_DATA="$("$VDEV_HELPERS/stat_input" "$VDEV_MOUNTPOINT/$VDEV_PATH")"
   STAT_RC=$?

   # succeeded?
   if [ $STAT_RC -ne 0 ]; then 
      vdev_error "$VDEV_HELPERS/stat_input $VDEV_PATH exit code $STAT_RC"
      return 20
   fi

   # import 
   eval "$INPUT_DATA"
   
   VDEV_INPUT_PROPERTIES="$VDEV_PROPERTIES"

   # get the persistent path for this device 
   # should set VDEV_PERSISTENT_PATH
   VDEV_PATH_PROPERTIES=""
   VDEV_PROPERTIES=""
   VDEV_PERSISTENT_PATH=""
   VDEV_PERSISTENT_PATH_TAG=""
   
   INPUT_DATA="$("$VDEV_HELPERS/stat_path" "$VDEV_MOUNTPOINT/$VDEV_PATH")"
   STAT_RC=$?

   # succeeded?
   if [ $STAT_RC -ne 0 ]; then
      
      vdev_unset $VDEV_INPUT_PROPERTIES
      vdev_error "$VDEV_HELPERS/stat_path $VDEV_PATH exit code $STAT_RC"
      return 20
   fi

   # import 
   eval "$INPUT_DATA"

   if [ -n "$VDEV_PROPERTIES" ]; then 
      VDEV_PATH_PROPERTIES="$VDEV_PROPERTIES"
   fi

   # input class?
   INPUT_CLASS="$VDEV_INPUT_CLASS"
   CLASSLESS_EVDEV=0

   if [ -z "$INPUT_CLASS" ]; then 
      
      # not a mouse, keyboard, or joystick.
      # pcspkr?
      INPUT_CLASS=$(test -n "$(vdev_drivers "$SYSFS_PATH" | /bin/grep "pcspkr")" && echo "spkr")
   fi

   if [ -z "$INPUT_CLASS" ] && [ -f "$SYSFS_PATH/name" ]; then 
      
      # not a mouse, keyboard, joystick, or PC speaker 
      # ir device?  check the sysfs name
      INPUT_CLASS=$(/bin/grep -i "dvb" "$SYSFS_PATH/name")
      if [ -n "$INPUT_CLASS" ]; then 
         
         INPUT_CLASS="ir"
      else 

         INPUT_CLASS=$(/bin/grep " IR " "$SYSFS_PATH/name")
         if [ -n "$INPUT_CLASS" ]; then 
         
            INPUT_CLASS="ir"
         fi
      fi
   fi

   # allow empty input class for event device nodes generated by platform and usb devices...
   if [ -z "$INPUT_CLASS" -a -n "$(vdev_subsystems "$SYSFS_PATH" | /bin/egrep "usb|platform")" -a -n "$(echo "$VDEV_OS_DEVNAME" | /bin/egrep "event*")" ]; then 

      # evdev input device with an input class we cannot place
      CLASSLESS_EVDEV=1
   fi

   # no path?
   if [ -z "$VDEV_PERSISTENT_PATH" ]; then 
      
      # just set up permissions and add properties.
      # no links to add.
      setup_input_permissions "$INPUT_CLASS"
      store_properties $VDEV_PATH_PROPERTIES $VDEV_INPUT_PROPERTIES
      vdev_unset $VDEV_PATH_PROPERTIES $VDEV_INPUT_PROPERTIES
      return 0
   fi


   # /dev/input/by-path/ links
   if [ -n "$INPUT_CLASS" ]; then

      # non-empty input class...
      # event?
      if [ -n "$(echo "$VDEV_OS_DEVNAME" | /bin/egrep "event*")" ]; then 

         vdev_symlink "../../$VDEV_PATH" "$VDEV_MOUNTPOINT/input/by-path/$VDEV_PERSISTENT_PATH-event-$INPUT_CLASS" "$VDEV_METADATA"
      else 

         vdev_symlink "../../$VDEV_PATH" "$VDEV_MOUNTPOINT/input/by-path/$VDEV_PERSISTENT_PATH-$INPUT_CLASS" "$VDEV_METADATA"
      fi

   elif [ $CLASSLESS_EVDEV -ne 0 ]; then 
      
      # evdev input device with no input class
      vdev_symlink "../../$VDEV_PATH" "$VDEV_MOUNTPOINT/input/by-path/$VDEV_PERSISTENT_PATH-event" "$VDEV_METADATA"
   fi


   # /dev/input/by-id links
   BUS=
   SERIAL=
   IFNO=
   BY_ID=

   # so, is this a USB device?
   if [ -n "$(echo "$VDEV_OS_DEVPATH" | /bin/grep 'usb')" ]; then 

      INPUT_DATA="$("$VDEV_HELPERS/stat_usb" "$VDEV_OS_SYSFS_MOUNTPOINT/$VDEV_OS_DEVPATH")"
      STAT_RC=$?

      if [ $STAT_RC -ne 0 ]; then 
         
         vdev_error "$VDEV_HELPERS/stat_usb $VDEV_OS_SYSFS_MOUNTPOINT/$VDEV_OS_DEVPATH exit code $STAT_RC"
         vdev_unset $VDEV_PATH_PROPERTIES $VDEV_INPUT_PROPERTIES
         return 20
      fi

      # import 
      eval "$INPUT_DATA"

      if [ -n "$VDEV_USB_SERIAL" ]; then 

         SERIAL=$VDEV_USB_SERIAL
         IFNO=$VDEV_USB_INTERFACE_NUM
         BUS="usb"
      fi

   fi

   # TODO: are there more kinds of devices (non-usb) that have /dev/input/by-id links???

   if [ -n "$BUS" -a -n "$SERIAL" ]; then 

      if [ -n "$INPUT_CLASS" ]; then 

         if [ -n "$(echo $VDEV_OS_DEVNAME | /bin/egrep "mouse|js")" ]; then 
            
            # mouse or joystick
            if [ -z "$IFNO" -o "$IFNO" = "00" ]; then 
                  
               BY_ID="$BUS-$SERIAL-$INPUT_CLASS"
            else
               
               BY_ID="$BUS-$SERIAL-if${IFNO}-$INPUT_CLASS"
            fi

         elif [ -n "$(echo $VDEV_OS_DEVNAME | /bin/egrep "event")" ]; then 
            
            # evdev 
            if [ -z "$IFNO" -o "$IFNO" = "00" ]; then 

               BY_ID="$BUS-$SERIAL-event-$INPUT_CLASS"
            else
                        
               BY_ID="$BUS-$SERIAL-if${IFNO}-event-$INPUT_CLASS"
            fi
         fi
         
      elif [ $CLASSLESS_EVDEV -ne 0 ] && [ -n "$IFNO" ]; then 
         
         # evdev input device with no class
         BY_ID="$BUS-$SERIAL-event-if${IFNO}"
      fi
   fi

   if [ -n "$BY_ID" ]; then 

      vdev_symlink "../../$VDEV_PATH" "$VDEV_MOUNTPOINT/input/by-id/$BY_ID" "$VDEV_METADATA" 
   fi


   # permissions...
   setup_input_permissions "$INPUT_CLASS"

   # device properties
   store_properties $VDEV_PATH_PROPERTIES $VDEV_INPUT_PROPERTIES

   # next loop 
   vdev_unset $VDEV_PATH_PROPERTIES $VDEV_INPUT_PROPERTIES
   
   return 0
}


if [ $VDEV_DAEMONLET -eq 0 ]; then 
   main 
   exit $?
fi
