#! /bin/bash
# Autodetection script for scanning /sys for hardware
# for Arch Linux by Tobias Powalowski <tpowa@archlinux.org>

_SHOW_MODULES=""
_SHOW_HOOKS=""
_HWKVER=""

usage () {
    echo "$0 [options]"
    echo ""
    echo " This is a tool that detects/lists modules that are exported by /sys"
    echo ""
    echo "  Options:"
    echo "    --kernel_version=      use kernel version (no autodetect)"
    echo "    --kernel_directory=    use kernel module directory (no autodetect)"
    echo "    --show-modules         show all detected modules"
    echo "    --show-modules-order   shows load order of detected modules"
    echo "    --show-agp             show AGP modules"
    echo "    --show-acpi            show ACPI modules"
    echo "    --show-block           show BLOCK DEVICE modules"
    echo "    --show-bluetooth       show BLUETOOTH modules"
    echo "    --show-cdrom           show CDROM modules"
    echo "    --show-cpufreq         show CPUFREQ modules"
    echo "    --show-crypto          show CRYPTO modules"
    echo "    --show-dca             show DCA modules"
    echo "    --show-dma             show DMA modules"
    echo "    --show-drm             show DRM modules"
    echo "    --show-edac            show EDAC modules"
    echo "    --show-events          show EVENTS modules"
    echo "    --show-hwmon           show HWMON modules"
    echo "    --show-i2c             show I2C modules"
    echo "    --show-input           show INPUT modules"
    echo "    --show-ipmi            show IPMI modules"
    echo "    --show-irda            show IRDA modules"
    echo "    --show-kvm             show KVM modules"
    echo "    --show-media           show MEDIA modules"
    echo "    --show-mei             show MEI modules"
    echo "    --show-mfd             show MFD modules"
    echo "    --show-mtd             show MTD modules"
    echo "    --show-net             show NETWORK modules"
    echo "    --show-nvme            show NVME modules"
    echo "    --show-parport         show PARPORT modules"
    echo "    --show-platform        show PLATFORM modules"
    echo "    --show-powercap        show POWERCAP modules"
    echo "    --show-serial          show SERIAL modules"
    echo "    --show-sound           show SOUND modules"
    echo "    --show-spi             show SPI modules"
    echo "    --show-staging         show STAGING modules"
    echo "    --show-thermal         show THERMAL modules"
    echo "    --show-tpm             show TPM modules"
    echo "    --show-video           show VIDEO modules"
    echo "    --show-virt            show VIRT modules"
    echo "    --show-watchdog        show WATCHDOG modules"
    echo "    --show-other           show OTHER modules"
    echo ""
    echo "  For /etc/mkinitcpio.conf use:"
    echo "    --filesystem           add filesystems to MODULES="
    echo "    --hostcontroller       show MODULES= for all hostcontrollers"
    echo "    --ati-kms              add ati kernel mode setting"
    echo "    --amd-kms              add amd kernel mode setting"
    echo "    --intel-kms            add intel kernel mode setting"
    echo "    --nvidia-kms           add nvidia kernel mode setting"
    echo ""
    echo "    --rootdevice=          autodetected advanced features of rootdevice"
    echo "    --hooks-dir=           use this directory for HOOKS check"
    echo "    --advanced             show autodetected advanced HOOKS"
    echo "    --hooks                show HOOKS="
    echo "    --keymap               add keymap to HOOKS="
    echo "    --nfs                  add net to HOOKS="
    exit 1
}

[ "$1" = "" ]       && usage
[ "$1" = "--help" ] && usage
[ "$1" = "-h" ]     && usage

# setting parameters
PARAMETER="$(echo $*)"
parameter() {
    while [ -n "$1" ]; do
        case $1 in
            --kernel_directory=*)
            KERNEL_DIRECTORY="$(echo $1 | awk -F= '{print $2;}')"
            ;;
            --kernel_version=*)
            KERNEL_VERSION="$(echo $1 | awk -F= '{print $2;}')"
            ;;
            --rootdevice=*)
            ROOTDEVICE="$(echo $1 | awk -F= '{print $2;}')"
            ;;
            -hooks-dir=*|--hooks-dir=*)
            HOOKS_DIR="$(echo $1 | awk -F= '{print $2;}')"
            ;;
        esac
        shift
    done
}

kver() {
    # get kernel version from installed kernel
    [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "riscv64" ]] && VMLINUZ=/boot/vmlinuz-linux
    [[ "$(uname -m)" == "aarch64" ]] && VMLINUZ=/boot/Image
    if [[ -f "${VMLINUZ}" && "$(uname -m)" == "x86_64" ]]; then
        offset="$(od -An -j0x20E -dN2 "${VMLINUZ}")"
        read -r _HWKVER _ < <(dd if="${VMLINUZ}" bs=1 count=127 skip=$((offset + 0x200)) 2>/dev/null)
    fi
    if [[ -f "${VMLINUZ}" ]]  &&  [[ "$(uname -m)" == "riscv64" || "$(uname -m)" == "aarch64" ]]; then
        reader="cat"
        # try if the image is gzip compressed
        bytes="$(od -An -t x2 -N2 "${VMLINUZ}" | tr -dc '[:alnum:]')"
        [[ $bytes == '8b1f' ]] && reader="zcat"
        read -r _ _ _HWKVER _ < <($reader "${VMLINUZ}" | grep -m1 -aoE 'Linux version .(\.[-[:alnum:]]+)+')
    fi
    # fallback if no detectable kernel is installed
    [[ "${_HWKVER}" == "" ]] && _HWKVER="$(uname -r)"
}

parameter $PARAMETER

if [ "$KERNEL_VERSION" = "" ]; then
        kver
    KERNEL_VERSION="${_HWKVER}"

fi

# keymap switch
[ "$(echo $* | grep '\-keymap')" ] && KEYMAP=1

# nfs switch
[ "$(echo $* | grep '\-nfs')" ] && NFS=1

# hooks switch
[ "$(echo $* | grep '\-hooks')" ] && HOOKS=1

ADVANCED=""
# root device check
if ! [ "$ROOTDEVICE" = "" ]; then
    ADVANCED="$(echo $(echo "$(lsblk -rpsno TYPE $ROOTDEVICE | grep -vwe "disk" -we "part")" | tac) | sed -e 's#crypt#encrypt#g' -e 's#raid.*[0-9]#mdadm_udev#g' -e 's#lvm#lvm2#g')"
fi

: >/tmp/modules-plain
: >/tmp/modules-stripped
: >/tmp/.blkid

# generate blkid
blkid -c /dev/null -s TYPE > /tmp/.blkid

# find modaliases
aliases=$(find /sys/ -noleaf -name modalias  -exec cat {} +)

# generate files for the different actions
modprobe -i -a --dirname=$KERNEL_DIRECTORY --set-version=$KERNEL_VERSION --show-depends $aliases >> /tmp/modules-plain 2>/dev/null
sort -u /tmp/modules-plain >> /tmp/modules-stripped

listmods() {
    key=$1 ; shift
    ex=
    while [ "$1" ]; do
        [ "$ex" ] && ex="$ex|$1" || ex="$1"
        shift
    done
    for ln in $(grep "$key" /tmp/modules-stripped | sed 's|^insmod ||g'); do
        if [ "$ex" ]; then
            echo $ln | grep -E -v "$ex" | sed -ne "s#^/.*/\(.*\)\.ko.*#\1#p"
        else
            echo $ln | sed -ne "s#^/.*/\(.*\)\.ko.*#\1#p"
        fi
    done
}
showlist() {
    cat=$1 ; shift
    [ $# -gt 0 ] || return
    echo -n "$cat: "
    for i in $*; do echo -n "$i "; done
    echo ""
}

showlist2() {
    cat=$1 ; shift
    [ $# -gt 0 ] || return
    echo -n "$cat=("
    for i in $*; do echo -n $i\ ; done
    echo ""
}

# starting different actions
while [ -n "$*"  ]; do
    case $1 in
        --show-modules)
            showlist "AGP      " $(listmods agp/)
            showlist "ACPI     " $(listmods acpi/)
            showlist "BLOCK    " $(listmods ata/pata pata_acpi) $(listmods ata/ata_piix) \
                         $(listmods virtio/virtio_pci) $(listmods scsi/) $(listmods message/fusion/) $(listmods drivers/block/ nbd pktcdvd sx8 floppy) \
                         $(listmods ata/ pata ata_generic) $(listmods drivers/block/sx8) \
                         $(listmods usb/ usb/input) $(listmods firewire/) $(listmods ieee1394/) $(listmods nvme.ko)
            showlist "BLUETOOTH" $(listmods bluetooth/)
            showlist "CDROM    " $(listmods cdrom/)
            showlist "CPUFREQ  " $(listmods cpufreq/)
            showlist "CRYPTO   " $(listmods crypto/)
            showlist "DCA      " $(listmods dca/)
            showlist "DMA      " $(listmods dma/)
            showlist "DRM      " $(listmods drm/)
            showlist "EDAC     " $(listmods edac/)
            showlist "EVENTS   " $(listmods events/)
            showlist "HWMON    " $(listmods hwmon/)
            showlist "I2C      " $(listmods i2c/)
            showlist "INPUT    " $(listmods input/ pcspkr) $(listmods hid/) $(listmods mac_hid)
            showlist "IPMI     " $(listmods ipmi/)
            showlist "IRDA     " $(listmods irda/)
            showlist "KVM      " $(listmods kvm/)
            showlist "MEDIA    " $(listmods media/)
            showlist "MEI      " $(listmods mei/)
            showlist "MFD      " $(listmods mfd/)
            showlist "MTD      " $(listmods mtd/)
            showlist "NET      " $(listmods net/ irda/)
            showlist "NVME     " $(listmods nvme/ nvme.ko)
            showlist "PARPORT  " $(listmods parport/)
            showlist "PLATFORM " $(listmods platform/)
            showlist "POWERCAP " $(listmods powercap/)
            showlist "SERIAL   " $(listmods serial/)
            showlist "SOUND    " $(listmods pcspkr) $(listmods sound/)
            showlist "SPI      " $(listmods spi/)
            showlist "STAGING  " $(listmods staging/)
            showlist "THERMAL  " $(listmods thermal/)
            showlist "TPM      " $(listmods tpm/)
            showlist "VIDEO    " $(listmods video/)
            showlist "VIRT     " $(listmods virt/)
            showlist "WATCHDOG " $(listmods watchdog/)
            showlist "OTHER    " $(listmods modules/ agp/ acpi/ scsi/ message/fusion block/sx8 block/cciss block/cpqarray block/DAC960 block/virtio virtio/virtio_pci ata/ \
                        usb/ ieee1394 bluetooth/ cdrom/ cpufreq/ crypto/ dca/ dma/ edac/ events/ net/ hwmon/ i2c/ input/ ipmi/ irda/ kvm/ mac_hid media/ mei/ \
                        mfd/ mtd/ nvme/ parport/ platform/ powercap/ sound/ spi/ thermal/ tpm/ drm/ firewire/ hid/ serial/ staging/ video/ virt/ watchdog/)
            ;;
        --show-modules-order)
            showlist "MODULES ORDER" $(listmods modules/)
            ;;
        --show-agp)       showlist "AGP      " $(listmods agp/) ;;
        --show-acpi)      showlist "ACPI     " $(listmods acpi/) ;;
        --show-block)     showlist "BLOCK    " $(listmods ata/pata pata_acpi) $(listmods ata/ata_piix) \
                               $(listmods virtio/virtio_pci) $(listmods scsi/) $(listmods message/fusion/) $(listmods drivers/block/ nbd pktcdvd sx8 floppy) \
                               $(listmods ata/ pata ata_generic) $(listmods drivers/block/sx8) \
                               $(listmods usb/ usb/input) $(listmods firewire/) $(listmods ieee1394/) $(listmods nvme.ko) ;;
        --show-bluetooth) showlist "BLUETOOTH" $(listmods bluetooth/) ;;
        --show-cdrom)     showlist "CDROM    " $(listmods cdrom/) ;;
        --show-cpufreq)   showlist "CPUFREQ  " $(listmods cpufreq/) ;;
        --show-crypto)    showlist "CRYPTO   " $(listmods crypto/) ;;
        --show-drm)       showlist "DRM      " $(listmods drm/) ;;
        --show-dca)       showlist "DCA      " $(listmods dca/) ;;
        --show-dma)       showlist "DMA      " $(listmods dma/) ;;
        --show-edac)      showlist "EDAC     " $(listmods edac/) ;;
        --show-events)    showlist "EVENTS   " $(listmods events/) ;;
        --show-hwmon)     showlist "HWMON    " $(listmods hwmon/) ;;
        --show-input)     showlist "INPUT    " $(listmods input/ pcspkr) $(listmods hid/) ;;
        --show-ipmi)      showlist "IPMI     " $(listmods ipmi/) ;;
        --show-i2c)       showlist "I2C      " $(listmods i2c/) ;;
        --show-irda)      showlist "IRDA     " $(listmods irda/) ;;
        --show-kvm)       showlist "KVM      " $(listmods kvm/) ;;
        --show-media)     showlist "MEDIA    " $(listmods media/) ;;
        --show-mei)       showlist "MEI      " $(listmods mei/) ;;
        --show-mfd)       showlist "MFD      " $(listmods mfd/) ;;
        --show-mtd)       showlist "MTD      " $(listmods mtd/) ;;
        --show-net)       showlist "NET      " $(listmods net/ irda/) ;;
        --show-nvme)      showlist "NVME     " $(listmods nvme/ nvme.ko) ;;
        --show-parport)   showlist "PARPORT  " $(listmods parport/) ;;
        --show-platform)  showlist "PLATFORM " $(listmods platform/) ;;
        --show-powercap)  showlist "POWERCAP " $(listmods powercap/) ;;
        --show-serial)    showlist "SERIAL   " $(listmods serial/) ;;
        --show-sound)     showlist "SOUND    " $(listmods pcspkr) $(listmods sound/) ;;
        --show-spi)       showlist "SPI      " $(listmods spi/) ;;
        --show-staging)   showlist "STAGING  " $(listmods staging/) ;;
        --show-thermal)   showlist "THERMAL  " $(listmods thermal/) ;;
        --show-tpm)       showlist "TPM      " $(listmods tpm/) ;;
        --show-video)     showlist "VIDEO    " $(listmods video/) ;;
        --show-virt)      showlist "VIRT     " $(listmods virt/) ;;
        --show-watchdog)  showlist "WATCHDOG " $(listmods watchdog/) ;;
        --show-other)     showlist "OTHER    " $(listmods .ko agp/ acpi/ scsi/ message/fusion block/sx8 block/cciss block/cpqarray block/DAC960 block/virtio virtio/virtio_pci ata/ \
                        usb/ ieee1394 bluetooth/ cdrom/ cpufreq/ crypto/ dca/ dma/ edac/ events/ net/ hwmon/ i2c/ input/ ipmi/ irda/ kvm/ mac_hid media/ mei/ \
                        mfd/ mtd/ nvme/ parport/ platform/ powercap/ sound/ thermal/ tpm/ drm/ firewire/ hid/ serial/ staging/ video/ virt/ watchdog/) ;;
        --filesystem)   FILESYSTEM="ext2 ext3 ext4 f2fs nilfs2 btrfs reiserfs xfs jfs vfat"
                for i in $FILESYSTEM; do
                     [ "$(grep $i /tmp/.blkid)" ] && FS="$FS $i"
                done
                [ "$(echo $FS | grep btrfs)" ] && FS="$FS crc32c"
                MODULES_INITRAMFS="$MODULES_INITRAMFS $FS"
                _SHOW_MODULES="1"
                ;;
        --hostcontroller)HOSTCONTROLLER="$(listmods virtio/virtio_pci) $(listmods ata/pata pata_acpi) $(listmods scsi/ /sg.ko /st.ko scsi_mod sr_mod sd_mod) $(listmods message/fusion/) $(listmods drivers/block/ virtio_blk nbd pktcdvd sx8 floppy) $(listmods ata/ pata ata_generic) $(listmods drivers/block/sx8) $(listmods xhci-hcd) $(listmods ehci-hcd) $(listmods uhci-hcd) $(listmods ohci-hcd) $(listmods virtio_blk) $(listmods nvme/) $(listmods xhci-pci)"
                 MODULES_INITRAMFS="$MODULES_INITRAMFS $HOSTCONTROLLER"
                 _SHOW_MODULES="1"
                 ;;
        --ati-kms)     KMS="radeon"
                MODULES_INITRAMFS="$KMS $MODULES_INITRAMFS"
                _SHOW_MODULES="1"
                ;;
        --amd-kms)     KMS="amdgpu"
                MODULES_INITRAMFS="$KMS $MODULES_INITRAMFS"
                _SHOW_MODULES="1"
                ;;
        --intel-kms)     KMS="i915"
                MODULES_INITRAMFS="$KMS $MODULES_INITRAMFS"
                _SHOW_MODULES="1"
                ;;
        --nvidia-kms)     KMS="nouveau"
                MODULES_INITRAMFS="$KMS $MODULES_INITRAMFS"
                _SHOW_MODULES="1"
                ;;
        --hooks)
            if [ "$HOOKS_DIR" = "" ]; then
                HOOKS_DIR="/usr/lib/initcpio/install"
            fi
            START_HOOKS="base udev autodetect modconf kms consolefont keyboard keymap block filesystems fsck net $ADVANCED"
            # remove the ones that don't exist on the system
            for i in ${START_HOOKS}; do
                if ! [ -e "${HOOKS_DIR}/$i" ]; then
                    START_HOOKS=$(echo $START_HOOKS | sed -e "s/${i}\ //g")
                fi
            done
            if ! [ "$KEYMAP" = "1" ]; then
                START_HOOKS=$(echo $START_HOOKS | sed -e "s/keymap//g")
            fi
            if ! [ "$NFS" = "1" ]; then
                START_HOOKS=$(echo $START_HOOKS | sed -e "s/net//g")
            fi
            _SHOW_HOOKS="1"
        ;;
        --advanced) echo "$ADVANCED"
                ;;
    esac
    shift
done

[[ "${_SHOW_MODULES}" == "1" ]] && showlist2 "MODULES" $MODULES_INITRAMFS | sed -e 's/(\  / /g' -e 's/\ \ /\ /g' -e 's/ $/)/g'
[[ "${_SHOW_HOOKS}" == "1" ]] && echo "HOOKS=($START_HOOKS " | sed -e 's/\ \ /\ /g' -e 's/\ $/)/g'

# cleanup
rm /tmp/modules-plain
rm /tmp/modules-stripped
rm /tmp/.blkid

# vim: set ts=2 sw=2 noet:
