diff --git a/site/gather-hardware-info.sh b/site/gather-hardware-info.sh index 3dc9c37..6fe70d5 100755 --- a/site/gather-hardware-info.sh +++ b/site/gather-hardware-info.sh @@ -91,7 +91,109 @@ if have lsblk && have findmnt; then fi fi -# --- WiFi chip: try PCI, then USB, then fall back to the driver name --- +# --- network device naming: sysfs first, lspci/lsusb layered on top --- +# lspci/lsusb aren't guaranteed to be installed (pciutils/usbutils are +# separate packages, often missing on minimal/live-boot systems) — but +# /sys is always there. Same approach as finding a chip by hand: read +# vendor/device IDs straight from sysfs, and use lspci/lsusb only when +# present to turn a hex ID like 0x8086 into "Intel" instead of guessing. + +pci_vendor_name() { + case "$1" in + 0x8086) echo "Intel" ;; + 0x14e4) echo "Broadcom" ;; + 0x10ec) echo "Realtek" ;; + 0x168c|0x1969) echo "Qualcomm Atheros" ;; + 0x02df|0x0e8d) echo "MediaTek" ;; + 0x1814) echo "Ralink" ;; + *) echo "" ;; + esac +} + +usb_vendor_name() { + case "$1" in + 8086) echo "Intel" ;; + 0a5c) echo "Broadcom" ;; + 0bda) echo "Realtek" ;; + 0cf3) echo "Qualcomm Atheros" ;; + 148f) echo "Ralink/MediaTek" ;; + *) echo "" ;; + esac +} + +# describe_net_device IFACE — best-effort human description of the hardware +# backing a network interface. Walks /sys/class/net/$iface/device to find +# whether it's a PCI, USB, or SDIO device, reads the IDs directly, and only +# reaches for lspci/lsusb (if installed) to prettify the name. +describe_net_device() { + local iface="$1" devpath realpath driver="" name="" + devpath="/sys/class/net/$iface/device" + [ -e "$devpath" ] || return 0 + realpath="$(readlink -f "$devpath")" + [ -r "$devpath/uevent" ] && driver="$(awk -F= '/^DRIVER=/ {print $2}' "$devpath/uevent")" + + case "$realpath" in + */pci*) + local vendor device pciid vname + vendor="$(cat "$devpath/vendor" 2>/dev/null)" + device="$(cat "$devpath/device" 2>/dev/null)" + # Not just basename: some drivers (virtio-net, and others that wrap a + # PCI function in a virtual sub-bus) resolve one level deeper than + # the actual PCI address, e.g. .../0000:00:12.0/virtio2 — the real + # slot is the LAST domain:bus:device.function-shaped path component, + # not necessarily the final one. Confirmed by hand: basename alone + # gave "virtio2", which lspci -s rejects as "Invalid slot number". + pciid="$(echo "$realpath" | grep -oE '[0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9a-f]' | tail -1)" + if have lspci && [ -n "$pciid" ]; then + name="$(lspci -s "$pciid" 2>/dev/null | sed -E 's/^[0-9a-f:.]+ [^:]+: //')" + fi + if [ -z "$name" ] && [ -n "$vendor" ]; then + vname="$(pci_vendor_name "$vendor")" + name="${vname:-PCI vendor $vendor} device $device" + fi + ;; + */usb*) + local usbdir idv idp vname + usbdir="$realpath" + # The net device's "device" symlink usually points at the USB + # *interface*, not the device itself — walk up until we find the + # level that actually has idVendor/idProduct. + while [ -n "$usbdir" ] && [ "$usbdir" != "/" ] && [ ! -r "$usbdir/idVendor" ]; do + usbdir="$(dirname "$usbdir")" + done + if [ -r "$usbdir/idVendor" ]; then + idv="$(cat "$usbdir/idVendor" 2>/dev/null)" + idp="$(cat "$usbdir/idProduct" 2>/dev/null)" + if have lsusb; then + name="$(lsusb -d "${idv}:${idp}" 2>/dev/null | head -1 | sed -E 's/^Bus [0-9]+ Device [0-9]+: ID [0-9a-f]{4}:[0-9a-f]{4} //')" + fi + if [ -z "$name" ] && [ -n "$idv" ]; then + vname="$(usb_vendor_name "$idv")" + name="${vname:-USB vendor $idv} device $idp" + fi + fi + ;; + */sdio*) + # SDIO WiFi (common on Raspberry Pi and other ARM SBCs, e.g. Broadcom + # BCM43455) has no standard human-readable name in sysfs — the driver + # (brcmfmac, etc.) is usually the most reliable identifier here. + local vendor device + vendor="$(cat "$devpath/vendor" 2>/dev/null)" + device="$(cat "$devpath/device" 2>/dev/null)" + [ -n "$vendor" ] && name="SDIO vendor $vendor device $device" + ;; + esac + + if [ -n "$name" ] && [ -n "$driver" ]; then + echo "$name (driver: $driver)" + elif [ -n "$name" ]; then + echo "$name" + elif [ -n "$driver" ]; then + echo "driver: $driver (chip model not identified)" + fi +} + +# --- WiFi chip --- wifi_chip="none" wifi_iface="" for w in /sys/class/net/*/wireless; do @@ -100,24 +202,22 @@ for w in /sys/class/net/*/wireless; do break done if [ -n "$wifi_iface" ]; then - if have lspci; then - wifi_chip="$(lspci | grep -iE 'network controller|wireless' | head -1 | sed -E 's/^[0-9a-f:.]+ [^:]+: //')" - fi - if [ -z "$wifi_chip" ] && have lsusb; then - wifi_chip="$(lsusb | grep -iE 'wireless|wifi|802\.11' | head -1 | sed -E 's/^Bus [0-9]+ Device [0-9]+: ID [0-9a-f]{4}:[0-9a-f]{4} //')" - fi - if [ -z "$wifi_chip" ] && [ -r "/sys/class/net/$wifi_iface/device/uevent" ]; then - driver="$(awk -F= '/^DRIVER=/ {print $2}' "/sys/class/net/$wifi_iface/device/uevent")" - [ -n "$driver" ] && wifi_chip="driver: $driver (chip model not auto-detected — check \`lsusb\`/\`lspci\` by hand)" - fi + wifi_chip="$(describe_net_device "$wifi_iface")" [ -z "$wifi_chip" ] && wifi_chip="TODO — wireless interface $wifi_iface found but chip not identified" fi -# --- Ethernet chip (best-effort) --- +# --- Ethernet chip: first non-wireless, non-loopback interface with a real +# device behind it (best-effort — on a box with a cellular modem and no +# wired NIC this could pick the modem; double-check if that's your setup) --- ethernet="" -if have lspci; then - ethernet="$(lspci | grep -i 'ethernet controller' | head -1 | sed -E 's/^[0-9a-f:.]+ [^:]+: //')" -fi +for e in /sys/class/net/*; do + iface="$(basename "$e")" + [ "$iface" = "lo" ] && continue + [ -d "$e/wireless" ] && continue + [ -e "$e/device" ] || continue + ethernet="$(describe_net_device "$iface")" + [ -n "$ethernet" ] && break +done today="$(date +%F 2>/dev/null || echo "TODO")"