#!/bin/sh
# enumerate-gpus.sh — JSON array of GPUs via /sys (no nvidia-smi)
# - Detects NVIDIA/other GPUs by PCI class (0x030000 VGA, 0x030200 3D)
# - Adds "model" from /proc/driver/nvidia/... when driver=nvidia
#   and falls back to /usr/share/{misc,hwdata}/pci.ids (if present)
# - Emits integers for iommuGroup, link widths; strings for link speeds

FIRST=true
printf '['

for DEV in /sys/bus/pci/devices/*; do
  [ -d "$DEV" ] || continue
  BDF=${DEV##*/}

  VENDOR=$(cat "$DEV/vendor" 2>/dev/null || echo "")
  CLASS=$(cat "$DEV/class" 2>/dev/null || echo "")

  # GPU classes: 0x030000 (VGA), 0x030200 (3D controller)
  case "$CLASS" in
    0x030*) ;;
    *) continue ;;
  esac

  DEVICE=$(cat "$DEV/device" 2>/dev/null || echo "")
  SUBV=$(cat "$DEV/subsystem_vendor" 2>/dev/null || echo "")
  SUBD=$(cat "$DEV/subsystem_device" 2>/dev/null || echo "")

  # Driver (nvidia/nouveau/…)
  DRIVER_PATH=$(readlink -f "$DEV/driver" 2>/dev/null || true)
  DRIVER=${DRIVER_PATH##*/}; [ "$DRIVER" = "/driver" ] && DRIVER=""

  # NUMA / IOMMU group
  NUMA=$(cat "$DEV/numa_node" 2>/dev/null || echo -1)
  IOMMU_PATH=$(readlink -f "$DEV/iommu_group" 2>/dev/null || true)
  IOMMU_GROUP=
  if [ -n "$IOMMU_PATH" ]; then
    base=${IOMMU_PATH##*/}
    case "$base" in ''|*[!0-9]*) IOMMU_GROUP= ;; *) IOMMU_GROUP="$base" ;; esac
  fi

  # DRM nodes
  DRM_CARD=""
  DRM_RENDER=""
  if [ -d "$DEV/drm" ]; then
    for d in "$DEV"/drm/*; do
      [ -e "$d" ] || continue
      base=$(basename "$d")
      case "$base" in
        card[0-9]*) DRM_CARD="$base" ;;
        renderD[0-9]*) DRM_RENDER="$base" ;;
      esac
    done
  fi

  # PCIe link info
  CUR_SPEED=$(cat "$DEV/current_link_speed" 2>/dev/null || echo "")
  CUR_WIDTH=$(cat "$DEV/current_link_width" 2>/dev/null || echo "")
  MAX_SPEED=$(cat "$DEV/max_link_speed" 2>/dev/null || echo "")
  MAX_WIDTH=$(cat "$DEV/max_link_width" 2>/dev/null || echo "")

  # /dev nodes (proprietary driver hints)
  DEV_NVIDIA=false
  DEV_NVIDIACTL=false
  DEV_NVIDIAUVM=false
  for n in /dev/nvidia[0-9]*; do
    [ -e "$n" ] || continue
    DEV_NVIDIA=true; break
  done
  [ -e /dev/nvidiactl ] && DEV_NVIDIACTL=true
  [ -e /dev/nvidia-uvm ] && DEV_NVIDIAUVM=true

  # VBIOS & Model via proprietary driver (if present)
  VBIOS=""
  MODEL=""
  if [ "$VENDOR" = "0x10de" ] && [ "$DRIVER" = "nvidia" ]; then
    INFO="/proc/driver/nvidia/gpus/$BDF/information"
    if [ -r "$INFO" ]; then
      VBIOS=$(grep -i '^Video BIOS' "$INFO" 2>/dev/null | sed 's/^[^:]*:[[:space:]]*//')
      MODEL=$(grep -i '^Model' "$INFO" 2>/dev/null | sed 's/^[^:]*:[[:space:]]*//')
    fi
  fi

  # Fallback model lookup via pci.ids (if still empty)
  if [ -z "$MODEL" ] && [ "$VENDOR" = "0x10de" ]; then
    for DB in /usr/share/misc/pci.ids /usr/share/hwdata/pci.ids; do
      [ -r "$DB" ] || continue
      model_try=$(awk -v vid="10de" -v did="${DEVICE#0x}" '
        BEGIN { FS=""; in_vendor=0 }
        /^[0-9a-fA-F]{4}[ \t]/ { in_vendor = (tolower(substr($0,1,4)) == vid); next }
        in_vendor && /^	[0-9a-fA-F]{4}[ \t]/ {
          if (tolower(substr($0,2,4)) == tolower(did)) {
            sub(/^	[0-9a-fA-F]{4}[ \t]+/, "", $0); print $0; exit
          }
        }
      ' "$DB")
      [ -n "$model_try" ] && MODEL="$model_try" && break
    done
  fi

  # Minimal JSON escaping for strings that can contain quotes/backslashes
  esc() { printf %s "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; }
  VBIOS_ESC=$( [ -n "$VBIOS" ] && esc "$VBIOS" || echo "" )
  MODEL_ESC=$( [ -n "$MODEL" ] && esc "$MODEL" || echo "" )

  # Emit JSON
  [ "$FIRST" = false ] && printf ','
  FIRST=false

  printf '{'
  printf '"pciBdf":"%s",' "$BDF"
  printf '"vendor":"%s",' "$VENDOR"
  printf '"device":"%s",' "$DEVICE"
  printf '"subvendor":"%s",' "$SUBV"
  printf '"subdevice":"%s",' "$SUBD"
  printf '"class":"%s",' "$CLASS"
  printf '"driver":"%s",' "$DRIVER"

  if [ -n "$IOMMU_GROUP" ]; then
    printf '"iommuGroup":%s,' "$IOMMU_GROUP"
  else
    printf '"iommuGroup":null,'
  fi

  printf '"numa":%s,' "$NUMA"

  # DRM block
  if [ -n "$DRM_CARD" ] || [ -n "$DRM_RENDER" ]; then
    printf '"drm":{'
    if [ -n "$DRM_CARD" ]; then printf '"card":"%s"' "$DRM_CARD"; else printf '"card":null'; fi
    if [ -n "$DRM_RENDER" ]; then printf ',"render":"%s"' "$DRM_RENDER"; else printf ',"render":null'; fi
    printf '},'
  else
    printf '"drm":null,'
  fi

  # PCIe link
  printf '"pcie":{'
  if [ -n "$CUR_SPEED" ]; then printf '"currentLinkSpeed":"%s",' "$CUR_SPEED"; else printf '"currentLinkSpeed":null,'; fi
  if [ -n "$CUR_WIDTH" ]; then printf '"currentLinkWidth":%s,' "$CUR_WIDTH"; else printf '"currentLinkWidth":null,'; fi
  if [ -n "$MAX_SPEED" ]; then printf '"maxLinkSpeed":"%s",' "$MAX_SPEED"; else printf '"maxLinkSpeed":null,'; fi
  if [ -n "$MAX_WIDTH" ]; then printf '"maxLinkWidth":%s' "$MAX_WIDTH"; else printf '"maxLinkWidth":null'; fi
  printf '},'

  # dev nodes
  printf '"devNodes":{"nvidia":%s,"nvidiactl":%s,"nvidiauvm":%s},' \
    "$( $DEV_NVIDIA && echo true || echo false )" \
    "$( $DEV_NVIDIACTL && echo true || echo false )" \
    "$( $DEV_NVIDIAUVM && echo true || echo false )"

  # vbios + model
  if [ -n "$VBIOS_ESC" ]; then printf '"vbios":"%s"' "$VBIOS_ESC"; else printf '"vbios":null'; fi
  printf ',"model":'
  if [ -n "$MODEL_ESC" ]; then printf '"%s"' "$MODEL_ESC"; else printf 'null'; fi

  printf '}'
done

printf ']\n'
