#!/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"
xorg_conf="/etc/X11/xorg.conf"

print_help(){
cat <<EOF

Autodetect videocard, show card name and recommended drivers.

Usage: $0 [<options>]

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

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

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 2>/dev/null |
  while read prefix pattern drv dummy; do
    case "$modalias" in
    $pattern)
      echo "$drv"
    ;;
    esac
  done | uniq | tr '\n' ' '
}

scan_lspci(){
  lspci -Dm |
  while read -r 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 [ -z "$show" -o "$show" = "current" ]; then
      [ -r "$xorg_conf" ] &&
        current="$(xconf -d "$xorg_conf")" ||
        current="not enough permissions to detect"
    fi

    if [ "$show" = "drivers" -o "$show" = "first" -o "$show" = "" ]; 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%% *}"
      ;;
      current)
        echo "$current"
      ;;
      "")
        echo "name:    $cardname"
        echo "drivers: $drivers"
        echo "current: $current"
      ;;
      *)
        echo "Error: unknown parameter to -s option" >&2
        exit 1
      ;;
    esac

    break
  done
}

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
  ;;
  *)
    echo "Error: unknown method" >&2
    exit 1
  ;;
esac
