#!/bin/dash
# vim: set sts=4 expandtab:
#
# vdev equivalent of udev 78-sound-card.rules:
# - on controlC* add/change, trigger a card "change" uevent (ATTR{../uevent}="change")
# - on card* change, publish SOUND_* / ID_* properties for libudev consumers

. "$VDEV_HELPERS/subr.sh"

# $1  control device devpath ( .../sound/cardN/controlCN )
card_devpath_from_control() {
   echo "${1%/controlC*}"
}

# $1  card devpath
card_sysfs_from_devpath() {
   echo "$VDEV_OS_SYSFS_MOUNTPOINT/$1"
}

# $1  card devpath ( .../sound/cardN )
card_number_from_devpath() {
   echo "$1" | /bin/sed -n 's/.*\/card\([0-9]\+\)$/\1/p'
}

# $1  card sysfs path
trigger_card_change() {
   local _card_uevent="$1/uevent"

   if [ -w "$_card_uevent" ]; then
      printf 'change\n' > "$_card_uevent"
   fi
}

# $1  property metadata directory
# $2  card sysfs path
# $3  card devpath
sound_card_set_properties() {
   local _METADATA _CARD_SYSFS _CARD_DEVPATH _CARD_NUM _SUBS _SKIP_PCI _PROP
   local _VENDOR _DEVICE _MODEL _MODEL_DB _SERIAL _IFNUM _INTERFACES _PCM_CLASS _BUS

   _METADATA="$1"
   _CARD_SYSFS="$2"
   _CARD_DEVPATH="$3"
   _SKIP_PCI=0
   _BUS=""

   /bin/mkdir -p "$_METADATA"
   /bin/rm -f "$_METADATA/properties"

   vdev_add_property "VDEV_OS_SOUND_INITIALIZED" "1" "$_METADATA"

   _SUBS="$(vdev_subsystems "$_CARD_SYSFS")"

   if [ -n "$(echo "$_SUBS" | /bin/grep 'usb')" ]; then
      _PROP="$("$VDEV_HELPERS/stat_usb" "$_CARD_SYSFS")"
      if [ $? -eq 0 ] && [ -n "$_PROP" ]; then
         eval "$_PROP"
         vdev_add_properties "$_METADATA" $VDEV_PROPERTIES
      fi
      vdev_add_property "VDEV_BUS" "usb" "$_METADATA"
      _BUS="usb"
      _SKIP_PCI=1
   fi

   if [ "$_SKIP_PCI" -eq 0 ] && [ -n "$(echo "$_SUBS" | /bin/grep 'firewire')" ]; then
      if [ -f "$_CARD_SYSFS/device/guid" ]; then
         _SERIAL="$(/bin/cat "$_CARD_SYSFS/device/guid")"
         vdev_add_property "VDEV_BUS" "firewire" "$_METADATA"
         _BUS="firewire"
         vdev_add_property "VDEV_SERIAL" "$_SERIAL" "$_METADATA"
         vdev_add_property "VDEV_SERIAL_SHORT" "$_SERIAL" "$_METADATA"
         if [ -f "$_CARD_SYSFS/device/vendor" ]; then
            vdev_add_property "VDEV_VENDOR_ID" "$(/bin/cat "$_CARD_SYSFS/device/vendor")" "$_METADATA"
         fi
         if [ -f "$_CARD_SYSFS/device/model" ]; then
            vdev_add_property "VDEV_MODEL_ID" "$(/bin/cat "$_CARD_SYSFS/device/model")" "$_METADATA"
         fi
         if [ -f "$_CARD_SYSFS/device/vendor_name" ]; then
            vdev_add_property "VDEV_VENDOR" "$(/bin/cat "$_CARD_SYSFS/device/vendor_name")" "$_METADATA"
         fi
         if [ -f "$_CARD_SYSFS/device/model_name" ]; then
            vdev_add_property "VDEV_MODEL" "$(/bin/cat "$_CARD_SYSFS/device/model_name")" "$_METADATA"
         fi
         _SKIP_PCI=1
      fi
   fi

   if [ "$_SKIP_PCI" -eq 0 ] && [ -n "$(echo "$_SUBS" | /bin/grep 'pci')" ]; then
      if [ -f "$_CARD_SYSFS/device/vendor" ] && [ -f "$_CARD_SYSFS/device/device" ]; then
         vdev_add_property "VDEV_BUS" "pci" "$_METADATA"
         _BUS="pci"
         vdev_add_property "VDEV_VENDOR_ID" "$(/bin/cat "$_CARD_SYSFS/device/vendor")" "$_METADATA"
         vdev_add_property "VDEV_MODEL_ID" "$(/bin/cat "$_CARD_SYSFS/device/device")" "$_METADATA"
         _SKIP_PCI=1
      fi
   fi

   if [ "$_SKIP_PCI" -eq 0 ] && [ -n "$(echo "$_SUBS" | /bin/grep 'platform')" ]; then
      vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "internal" "$_METADATA"
      return 0
   fi

   _SERIAL=""
   _IFNUM=""
   if [ -f "$_METADATA/properties" ]; then
      _SERIAL="$(/bin/grep '^VDEV_SERIAL=' "$_METADATA/properties" | /bin/tail -1 | /bin/sed 's/^VDEV_SERIAL=//')"
      _IFNUM="$(/bin/grep '^VDEV_USB_INTERFACE_NUM=' "$_METADATA/properties" | /bin/tail -1 | /bin/sed 's/^VDEV_USB_INTERFACE_NUM=//')"
   fi

   if [ -n "$_BUS" ] && [ -n "$_SERIAL" ]; then
      if [ -n "$_IFNUM" ]; then
         vdev_add_property "VDEV_ID" "${_BUS}-${_SERIAL}-${_IFNUM}" "$_METADATA"
      else
         vdev_add_property "VDEV_ID" "${_BUS}-${_SERIAL}" "$_METADATA"
      fi
   fi

   _CARD_NUM="$(card_number_from_devpath "$_CARD_DEVPATH")"
   if [ -n "$_CARD_NUM" ] && [ -f "$_CARD_SYSFS/pcmC${_CARD_NUM}D0p/pcm_class" ]; then
      _PCM_CLASS="$(/bin/cat "$_CARD_SYSFS/pcmC${_CARD_NUM}D0p/pcm_class")"
      if [ "$_PCM_CLASS" = "modem" ]; then
         vdev_add_property "VDEV_OS_SOUND_CLASS" "modem" "$_METADATA"
         return 0
      fi
   fi

   if [ -n "$(echo "$_CARD_DEVPATH" | /bin/egrep '/0000:00:[0-9a-fA-F]{2}\.[0-9]/sound/')" ]; then
      vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "internal" "$_METADATA"
      return 0
   fi

   _INTERFACES=""
   _MODEL=""
   _MODEL_DB=""
   if [ -f "$_METADATA/properties" ]; then
      _INTERFACES="$(/bin/grep '^VDEV_USB_INTERFACES=' "$_METADATA/properties" | /bin/tail -1 | /bin/sed 's/^VDEV_USB_INTERFACES=//')"
      _MODEL="$(/bin/grep '^VDEV_MODEL=' "$_METADATA/properties" | /bin/tail -1 | /bin/sed 's/^VDEV_MODEL=//')"
      _MODEL_DB="$(/bin/grep '^VDEV_MODEL_FROM_DATABASE=' "$_METADATA/properties" | /bin/tail -1 | /bin/sed 's/^VDEV_MODEL_FROM_DATABASE=//')"
   fi

   if [ -n "$_INTERFACES" ] && [ -n "$(echo "$_INTERFACES" | /bin/egrep ':0e[0-9a-fA-F]{4}:')" ]; then
      vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "webcam" "$_METADATA"
      return 0
   fi

   for _PROP in "$_MODEL" "$_MODEL_DB"; do
      case "$_PROP" in
      *[Ss]peaker*)
         vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "speaker" "$_METADATA"
         return 0
         ;;
      *[Hh]eadphone*)
         vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "headphone" "$_METADATA"
         return 0
         ;;
      *[Hh]eadset*)
         vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "headset" "$_METADATA"
         return 0
         ;;
      *[Hh]andset*)
         vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "handset" "$_METADATA"
         return 0
         ;;
      *[Mm]icrophone*)
         vdev_add_property "VDEV_OS_SOUND_FORM_FACTOR" "microphone" "$_METADATA"
         return 0
         ;;
      esac
   done

   return 0
}

# entry point
main() {
   local _CARD_DEVPATH _CARD_SYSFS _CARD_METADATA _CARD_ID

   if [ "$VDEV_ACTION" = "remove" ]; then
      return 0
   fi

   if [ -n "$(echo "$VDEV_PATH" | /bin/egrep '^snd/controlC[0-9]+$')" ]; then
      if [ "$VDEV_ACTION" = "add" ] || [ "$VDEV_ACTION" = "change" ]; then
         _CARD_DEVPATH="$(card_devpath_from_control "$VDEV_OS_DEVPATH")"
         _CARD_SYSFS="$(card_sysfs_from_devpath "$_CARD_DEVPATH")"
         trigger_card_change "$_CARD_SYSFS"
      fi
      return 0
   fi

   if [ -z "$(echo "$VDEV_OS_DEVPATH" | /bin/egrep '/sound/card[0-9]+$')" ]; then
      return 0
   fi

   if [ "$VDEV_ACTION" != "change" ]; then
      return 0
   fi

   _CARD_DEVPATH="$VDEV_OS_DEVPATH"
   _CARD_SYSFS="$(card_sysfs_from_devpath "$_CARD_DEVPATH")"

   if [ "$VDEV_PATH" = "UNKNOWN" ]; then
      if [ -z "${VDEV_METADATA:-}" ]; then
         _CARD_ID="$(vdev_device_id)"
         if [ $? -ne 0 ] || [ -z "$_CARD_ID" ]; then
            vdev_warn "sound-card: no device id for $VDEV_OS_DEVPATH"
            return 0
         fi
         VDEV_METADATA="$VDEV_GLOBAL_METADATA/dev/$_CARD_ID"
      fi
      /bin/mkdir -p "$VDEV_METADATA"
   fi

   sound_card_set_properties "$VDEV_METADATA" "$_CARD_SYSFS" "$_CARD_DEVPATH"
   return 0
}

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