#!/bin/sh -eu

# Autodetect videocard, show card name and recommended drivers

. shell-getopt

method="lspci"
show=""

vc_dbdir="/usr/share/hwdatabase/videoaliases"
drv_pri="/usr/share/alterator-x11/priorities"
drv_dir="$(getconf LIBDIR)/X11/modules/drivers"

print_help(){
cat <<EOF

Autodetect videocard, show card name and recommended drivers.

Usage: $0 [<options>]

Options:
  -h                      -- show this help message
  -m (lspci|hal)          -- specify detection method
  -s (name|drivers|first) -- show only card name, driver list or first driver

Methods:
  lspci -- use lspci, sysfs and /usr/share/hwdatabase/videoaliases database
  hal   -- use hal

Fallback drivers, vesa and fbdev, are also shown.
Drivers are sorted according to /usr/share/alterator-x11/priorities database.
Only drivers which exists in /usr/lib/X11/modules/drivers are shown.
fbdev driver is shown only if /dev/fb0 exists.

EOF
}

# helper for selection specified field from parameter string
select_field(){
  local var=$1
  shift $2
  eval $var=\"$2\"
}

# sort drivers according to /usr/share/alterator-x11/priorities
resort_drivers(){
  local drivers="$@";
  local new_drivers;
  while read drv; do
    [ -n "${drv%#*}" ] || continue
    [ -z "${drivers##* $drv *}" ] || continue
    drivers="${drivers% $drv *} ${drivers#* $drv }"
    new_drivers="$new_drivers$drv "
  done < "$drv_pri"
  new_drivers="$new_drivers$drivers"

  for d in $new_drivers; do
    [ -f "$drv_dir/${d}_drv.so" ] || continue
    [ "$d" != "fbdev" -o -c "/dev/fb0" ] || continue
    echo -n "$d "
  done
}

find_drivers_by_modalias(){
  local modalias="$1"
  local prefix pattern drv dummy
  sed -e '/^alias/!d' "$vc_dbdir"/*.xinf |
  while read prefix pattern drv dummy; do
    case "$modalias" in
    $pattern)
      echo "$drv"
    ;;
    esac
  done | uniq | tr '\n' ' '
}

scan_lspci(){
  lspci -Dm |
  while read params; do
    eval select_field cap 2 $params
    [ "$cap" = "VGA compatible controller" ] || continue

    eval select_field pcidev   1 $params
    eval select_field cardname 4 $params

    if [ "$show" != "name" ]; then
      modalias="$(sed -e 's/^pci:/pcivideo:/' /sys/bus/pci/devices/$pcidev/modalias)"
      drivers="$(find_drivers_by_modalias $modalias) vesa fbdev"
      drivers="$(resort_drivers "$drivers")"
    fi

    case "$show" in
      name)
        echo "$cardname"
      ;;
      drivers)
        echo "$drivers"
      ;;
      first)
        echo "${drivers%% *}"
      ;;
      "")
        echo "name:    $cardname"
        echo "drivers: $(resort_drivers "$drivers")"
      ;;
      *)
        echo "Error: unknown parameter to -s option" >&2
        exit 1
      ;;
    esac

    break
  done
}

scan_hal(){
  udi="$(hal-find-by-capability --capability 'vga')"

  if [ "$show" != "name" ]; then
    drivers="$(hal-get-property --udi $udi --key video.x11_driver) vesa fbdev"
    drivers="$(resort_drivers "$drivers")"
  fi

  [ "$show" = "driver" -o "$show" = "first" ]  ||\
    cardname="$(hal-get-property --udi $udi --key info.product)"

  case "$show" in
    name)
      echo "$cardname"
    ;;
    drivers)
      echo "$drivers"
    ;;
    first)
      echo "${drivers%% *}"
    ;;
    "")
      echo "name:    $cardname"
      echo "drivers: $(resort_drivers "$drivers")"
    ;;
    *)
      echo "Error: unknown parameter to -s option" >&2
      exit 1
    ;;
  esac
}


while getopts "hm:s:" "$@"; do
  case $OPTOPT in
    h)
      print_help
      exit 1
    ;;
    m)
      method="$OPTARG"
    ;;
    s)
      show="$OPTARG"
    ;;
  esac
done

case "$method" in
  lspci)
    scan_lspci
  ;;
  hal)
    scan_hal
  ;;
  *)
    echo "Error: unknown method" >&2
    exit 1
  ;;
esac
