From aa9d18bf7d9ff9e9fd8c097a63c907c768f24e64 Mon Sep 17 00:00:00 2001 From: Hang Zhao <259427778+hangzqcom@users.noreply.github.com> Date: Wed, 8 Apr 2026 23:19:03 -0700 Subject: [PATCH 1/3] Add Debian packaging (DKMS) for Linux kernel drivers Add .deb package support using DKMS to handle install, uninstall, upgrade, and version control of Qualcomm USB kernel drivers on Debian/Ubuntu systems. New files: - debian/: Full Debian packaging (control, rules, changelog, maintainer scripts) - build-deb.sh: Build script that syncs version from version.h - dkms.conf: DKMS configuration for 4 kernel modules Features: - Builds qtiDevInf, qcom_usb, qcom_usbnet, qcom-serial via DKMS - Auto-rebuilds modules on kernel updates - Blacklists conflicting in-tree modules (qcserial, qmi_wwan, etc.) - Detects and removes conflicting userspace drivers (deb package, /opt/qcom/qcom_userspace, /opt/qcom/QUD, /opt/QTI/QUD, qcom-qud.service) - Cleans up pre-existing manual installs before installation - Installs udev rules for device permissions - Loads modules immediately after installation Updated: - README.md: Added deb package as recommended Linux install method - src/linux/README.md: Added deb package documentation - src/linux/version.h: Version 1.0.6.1 Signed-off-by: Hang Zhao <259427778+hangzqcom@users.noreply.github.com> --- README.md | 59 +- build-deb.sh | 65 ++ debian/.gitignore | 7 + debian/60-qcom-usb-drivers.rules | 10 + debian/changelog | 8 + debian/control | 23 + debian/copyright | 23 + debian/qcom-usb-drivers-dkms.dkms | 1 + debian/qcom-usb-drivers-dkms.modprobe | 16 + debian/qcom-usb-drivers-dkms.postinst | 43 + debian/qcom-usb-drivers-dkms.preinst | 191 +++++ debian/qcom-usb-drivers-dkms.prerm | 30 + debian/rules | 65 ++ debian/source/format | 1 + dkms.conf | 27 + src/linux/QcDevDriver.sh | 1083 +++++++++++++++++++++++++ src/linux/README.md | 60 +- src/linux/version.h | 2 +- 18 files changed, 1705 insertions(+), 9 deletions(-) create mode 100755 build-deb.sh create mode 100644 debian/.gitignore create mode 100644 debian/60-qcom-usb-drivers.rules create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/qcom-usb-drivers-dkms.dkms create mode 100644 debian/qcom-usb-drivers-dkms.modprobe create mode 100755 debian/qcom-usb-drivers-dkms.postinst create mode 100644 debian/qcom-usb-drivers-dkms.preinst create mode 100755 debian/qcom-usb-drivers-dkms.prerm create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 dkms.conf create mode 100644 src/linux/QcDevDriver.sh diff --git a/README.md b/README.md index 411cc16..6a65312 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,12 @@ The project is organized to facilitate easy compilation, testing, and integratio ``` / +├─ debian/ # Debian packaging files for .deb package build ├─ docs/ # Architecture diagrams and design documents ├─ src/ # Qualcomm USB kernel driver for windows and linux platform -├─ examples/ # samples scripts +├─ examples/ # Sample scripts +├─ build-deb.sh # Build script for generating the .deb package +├─ dkms.conf # DKMS configuration for kernel module management ├─ README.md # This file └─ ... # Other files and directories ``` @@ -33,6 +36,7 @@ The project is organized to facilitate easy compilation, testing, and integratio - GNU Make, GCC/Clang. - Kernel headers for the target kernel version (`linux-headers-$(uname -r)`). +- For deb package build: `debhelper`, `dkms`, `dpkg-dev`, `dh-dkms`. ### Build Steps @@ -86,9 +90,51 @@ pnputil /add-driver /install ```bash pnputil /delete-driver oemxx.inf /uninstall /force ``` -#### Linux command: - Navigate to folder `src/linux` - + +#### Linux — Debian Package (Recommended) + +The recommended way to install on Debian/Ubuntu systems is via the `.deb` package, which uses DKMS to automatically build kernel modules for the running kernel and handles install, uninstall, upgrade, and version control. + +- Build the package +```bash +# Install build dependencies (one-time) +sudo apt-get install debhelper dkms dpkg-dev dh-dkms + +# Build +./build-deb.sh +``` + +- Installation +```bash +sudo dpkg -i ../qcom-usb-drivers-dkms__all.deb +``` + +- Uninstallation +```bash +sudo dpkg -r qcom-usb-drivers-dkms +``` + +- Check installed version +```bash +dpkg -s qcom-usb-drivers-dkms | grep Version +dkms status +``` + +- Upgrade + + Simply install a newer `.deb` — it automatically removes the old version and installs the new one. + +**What the deb package does:** +- Builds all 4 kernel modules (`qtiDevInf`, `qcom_usb`, `qcom_usbnet`, `qcom-serial`) via DKMS +- Automatically rebuilds modules when the kernel is updated +- Installs udev rules and blacklists conflicting in-tree modules +- Handles cleanup of pre-existing manual installs +- Loads modules immediately after installation + +#### Linux — Manual Install (Alternative) + +Navigate to folder `src/linux` + - Installation ```bash sudo ./qcom_drivers.sh install @@ -97,7 +143,8 @@ sudo ./qcom_drivers.sh install ```bash sudo ./qcom_drivers.sh uninstall ``` -For more guidance on build process, FAQ's and troubleshooting, please refer to [README](./src/linux/README.md) document. + +For more guidance on build process, FAQ's and troubleshooting, please refer to [README](./src/linux/README.md) document. ## Contributing @@ -119,4 +166,4 @@ and conditions before contributing. ## Contact -For questions, bug reports, or feature requests, please open an issue on GitHub or contact the maintainers +For questions, bug reports, or feature requests, please open an issue on GitHub or contact the maintainers \ No newline at end of file diff --git a/build-deb.sh b/build-deb.sh new file mode 100755 index 0000000..925700d --- /dev/null +++ b/build-deb.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Build script for qcom-usb-drivers-dkms debian package +# Usage: ./build-deb.sh +# +# Prerequisites: sudo apt-get install debhelper dkms dpkg-dev dh-dkms +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +PKG_VERSION=$(grep '#define DRIVER_VERSION' src/linux/version.h | awk '{print $3}' | tr -d '"') +echo "Building qcom-usb-drivers-dkms version $PKG_VERSION" + +# Update version in dkms.conf if needed +sed -i "s/^PACKAGE_VERSION=.*/PACKAGE_VERSION=\"${PKG_VERSION}\"/" dkms.conf + +# Update version in debian/changelog if needed +CHANGELOG_VER=$(head -1 debian/changelog | grep -oP '\(\K[^)]+') +if [ "$CHANGELOG_VER" != "$PKG_VERSION" ]; then + sed -i "1s/([^)]*)/($PKG_VERSION)/" debian/changelog +fi + +# Update version in postinst and preinst if needed +sed -i "s/^PKG_VERSION=.*/PKG_VERSION=\"${PKG_VERSION}\"/" debian/qcom-usb-drivers-dkms.postinst +sed -i "s/^PKG_VERSION=.*/PKG_VERSION=\"${PKG_VERSION}\"/" debian/qcom-usb-drivers-dkms.preinst + +# Update version in debian/rules if needed +sed -i "s/^PKG_VERSION := .*/PKG_VERSION := ${PKG_VERSION}/" debian/rules + +# Check for required build tools +for tool in dpkg-buildpackage debhelper dkms; do + if [ "$tool" = "debhelper" ]; then + dpkg -l debhelper >/dev/null 2>&1 || { echo "Error: $tool not installed. Run: sudo apt-get install $tool"; exit 1; } + elif [ "$tool" = "dkms" ]; then + dpkg -l dkms >/dev/null 2>&1 || { echo "Error: $tool not installed. Run: sudo apt-get install $tool"; exit 1; } + else + command -v $tool >/dev/null 2>&1 || { echo "Error: $tool not found. Run: sudo apt-get install dpkg-dev"; exit 1; } + fi +done + +# Check for dh-dkms (provides dh_dkms helper) +dpkg -l dh-dkms >/dev/null 2>&1 || { echo "Error: dh-dkms not installed. Run: sudo apt-get install dh-dkms"; exit 1; } + +# Ensure debian/rules is executable +chmod +x debian/rules + +# Clean previous build artifacts +rm -f ../qcom-usb-drivers-dkms_*.deb +rm -f ../qcom-usb-drivers-dkms_*.buildinfo +rm -f ../qcom-usb-drivers-dkms_*.changes +rm -rf debian/qcom-usb-drivers-dkms +rm -rf debian/.debhelper + +# Build the package (unsigned) +dpkg-buildpackage -us -uc -b + +echo "" +echo "========================================" +echo "Build complete! Package files:" +ls -la ../qcom-usb-drivers-dkms_${PKG_VERSION}*.deb 2>/dev/null || echo "(deb file in parent directory)" +echo "" +echo "Install with: sudo dpkg -i ../qcom-usb-drivers-dkms_${PKG_VERSION}_all.deb" +echo "Uninstall with: sudo dpkg -r qcom-usb-drivers-dkms" +echo "Check status: dpkg -s qcom-usb-drivers-dkms" +echo "========================================" \ No newline at end of file diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000..e5ce6e6 --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,7 @@ +# Build artifacts generated by dpkg-buildpackage +.debhelper/ +debhelper-build-stamp +files +*.substvars +*.debhelper +qcom-usb-drivers-dkms/ diff --git a/debian/60-qcom-usb-drivers.rules b/debian/60-qcom-usb-drivers.rules new file mode 100644 index 0000000..a49b76d --- /dev/null +++ b/debian/60-qcom-usb-drivers.rules @@ -0,0 +1,10 @@ +# Qualcomm USB device udev rules +# GobiNet network device +SUBSYSTEM=="net", DRIVERS=="GobiNet", MODE="0660", GROUP="plugdev" + +# GobiSerial serial ports +SUBSYSTEM=="tty", DRIVERS=="GobiSerial", MODE="0660", GROUP="dialout" + +# QDSS diagnostic devices +SUBSYSTEM=="usb", DRIVERS=="QdssDiag", MODE="0660", GROUP="plugdev" +SUBSYSTEM=="usb", DRIVERS=="QdssMdm", MODE="0660", GROUP="plugdev" \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..c6b072c --- /dev/null +++ b/debian/changelog @@ -0,0 +1,8 @@ +qcom-usb-drivers-dkms (1.0.6.1) unstable; urgency=medium + + * Initial DKMS debian package for Qualcomm USB kernel drivers + * Includes qtiDevInf, qcom_usb, qcom_usbnet, qcom-serial modules + * Blacklists conflicting in-tree modules (qcserial, qmi_wwan, option, usb_wwan) + * Installs udev rules for Qualcomm USB device permissions + + -- Qualcomm Technologies, Inc. Tue, 08 Apr 2025 10:00:00 -0700 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..f37d627 --- /dev/null +++ b/debian/control @@ -0,0 +1,23 @@ +Source: qcom-usb-drivers-dkms +Section: kernel +Priority: optional +Maintainer: Qualcomm Technologies, Inc. +Build-Depends: debhelper-compat (= 13), dkms +Standards-Version: 4.6.0 +Rules-Requires-Root: no + +Package: qcom-usb-drivers-dkms +Architecture: all +Depends: ${misc:Depends}, dkms, linux-headers-generic | linux-headers-amd64 | linux-headers-arm64 +Conflicts: qcom-usb-userspace-drivers, qcom-usb-userspace-drivers-dkms +Replaces: qcom-usb-userspace-drivers, qcom-usb-userspace-drivers-dkms +Description: Qualcomm USB kernel drivers (DKMS) + DKMS package for Qualcomm USB kernel drivers including: + * qtiDevInf - INF file parser for device enumeration + * qcom_usb - USB diagnostic and QDSS driver + * qcom_usbnet - USB network (WWAN) adapter driver + * qcom-serial - USB serial/modem port driver + . + These drivers are automatically built for the running kernel using DKMS + and will be rebuilt when the kernel is updated. Conflicting in-tree + modules (qcserial, qmi_wwan, option, usb_wwan) are blacklisted. \ No newline at end of file diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..e86c1f5 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,23 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: qcom-usb-drivers +Upstream-Contact: Qualcomm Technologies + +Files: * +Copyright: 2025-2026 Qualcomm Technologies, Inc. and/or its subsidiaries. +License: BSD-3-Clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + . + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. \ No newline at end of file diff --git a/debian/qcom-usb-drivers-dkms.dkms b/debian/qcom-usb-drivers-dkms.dkms new file mode 100644 index 0000000..bb29b14 --- /dev/null +++ b/debian/qcom-usb-drivers-dkms.dkms @@ -0,0 +1 @@ +dkms.conf \ No newline at end of file diff --git a/debian/qcom-usb-drivers-dkms.modprobe b/debian/qcom-usb-drivers-dkms.modprobe new file mode 100644 index 0000000..16c12a6 --- /dev/null +++ b/debian/qcom-usb-drivers-dkms.modprobe @@ -0,0 +1,16 @@ +# Qualcomm USB kernel drivers - module configuration +# Installed by qcom-usb-drivers-dkms package + +# Blacklist conflicting in-tree modules so that Qualcomm drivers are used +blacklist qcserial +install qcserial /bin/false +blacklist qmi_wwan +install qmi_wwan /bin/false +blacklist option +install option /bin/false +blacklist usb_wwan +install usb_wwan /bin/false + +# Load Qualcomm USB drivers at boot (qtiDevInf must load first) +softdep qcom_usb pre: qtiDevInf +softdep qcom_usbnet pre: qtiDevInf \ No newline at end of file diff --git a/debian/qcom-usb-drivers-dkms.postinst b/debian/qcom-usb-drivers-dkms.postinst new file mode 100755 index 0000000..c8ed243 --- /dev/null +++ b/debian/qcom-usb-drivers-dkms.postinst @@ -0,0 +1,43 @@ +#!/bin/bash +# Post-installation script for qcom-usb-drivers-dkms +set -e + +PKG_NAME="qcom-usb-drivers" +PKG_VERSION="1.0.6.1" + +# DEBHELPER runs dkms add/build/install here +#DEBHELPER# + +case "$1" in + configure) + # Reload udev rules + udevadm control --reload-rules 2>/dev/null || true + udevadm trigger 2>/dev/null || true + + # Update module dependencies + depmod -a 2>/dev/null || true + + # Unload conflicting in-tree modules if loaded + for mod in qcserial qmi_wwan cdc_wdm option usb_wwan; do + if lsmod | grep -qw "$mod"; then + echo " Unloading conflicting module: $mod" + rmmod "$mod" 2>/dev/null || true + fi + done + + # Load the Qualcomm drivers (order matters: qtiDevInf first) + for mod in qtiDevInf qcom_usb qcom_usbnet qcom-serial; do + modprobe "$mod" 2>/dev/null || true + done + + echo "qcom-usb-drivers $PKG_VERSION: modules installed and loaded successfully." + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + ;; +esac + +exit 0 \ No newline at end of file diff --git a/debian/qcom-usb-drivers-dkms.preinst b/debian/qcom-usb-drivers-dkms.preinst new file mode 100644 index 0000000..000b9a2 --- /dev/null +++ b/debian/qcom-usb-drivers-dkms.preinst @@ -0,0 +1,191 @@ +#!/bin/bash +# Pre-install: detect conflicting userspace drivers, clean up pre-existing modules +set -e + +PKG_NAME="qcom-usb-drivers" +PKG_VERSION="1.0.6.1" + +# Our module names +MODULES="qtiDevInf qcom_usb qcom_usbnet qcom-serial" + +# Userspace driver detection paths +USERSPACE_DEB_PATTERN="qcom-usb-userspace" +USERSPACE_INSTALL_DIR="/opt/qcom/qcom_userspace" +OLD_QUD_DIR="/opt/qcom/QUD" +LEGACY_QUD_DIR="/opt/QTI/QUD" +USERSPACE_SERVICE="qcom-qud.service" +USERSPACE_MODULES="GobiNet QdssDiag" +USERSPACE_CONFIG="/etc/qcom_libusb.conf" + +case "$1" in + install|upgrade) + echo "qcom-usb-drivers: preparing installation (version $PKG_VERSION)..." + + # --- Step 0: Detect and remove conflicting userspace drivers --- + USERSPACE_FOUND=false + + # Check for userspace deb package + USERSPACE_PKG=$(dpkg -l 2>/dev/null | grep -i "$USERSPACE_DEB_PATTERN" | awk '{print $2}' | head -1) + if [ -n "$USERSPACE_PKG" ]; then + USERSPACE_FOUND=true + echo "" + echo " WARNING: Conflicting userspace driver package detected: $USERSPACE_PKG" + echo " The kernel-mode drivers are incompatible with the userspace drivers." + echo " Removing userspace driver package: $USERSPACE_PKG ..." + dpkg -r "$USERSPACE_PKG" 2>/dev/null || true + echo " Userspace driver package removed." + fi + + # Check for manually installed userspace drivers + if [ -d "$USERSPACE_INSTALL_DIR" ]; then + USERSPACE_FOUND=true + echo "" + echo " WARNING: Userspace driver installation detected at: $USERSPACE_INSTALL_DIR" + + # Try to use the uninstall script if available + if [ -x "$USERSPACE_INSTALL_DIR/qcom_drivers.sh" ]; then + echo " Running userspace driver uninstaller..." + "$USERSPACE_INSTALL_DIR/qcom_drivers.sh" uninstall 2>/dev/null || true + fi + + # Clean up remaining files + echo " Removing userspace driver directory: $USERSPACE_INSTALL_DIR" + rm -rf "$USERSPACE_INSTALL_DIR" + fi + + # Check for old QUD installation (/opt/qcom/QUD) + if [ -d "$OLD_QUD_DIR" ]; then + # Check if it has the userspace uninstaller (not the kernel driver one) + if [ -f "$OLD_QUD_DIR/qcom_drivers.sh" ]; then + USERSPACE_FOUND=true + echo "" + echo " WARNING: Old QUD installation detected at: $OLD_QUD_DIR" + echo " Running old QUD uninstaller..." + bash "$OLD_QUD_DIR/qcom_drivers.sh" uninstall 2>/dev/null || true + echo " Removing old QUD directory: $OLD_QUD_DIR" + rm -rf "$OLD_QUD_DIR" + fi + fi + + # Check for legacy QTI installation (/opt/QTI/QUD) + if [ -d "$LEGACY_QUD_DIR" ]; then + USERSPACE_FOUND=true + echo "" + echo " WARNING: Legacy QTI installation detected at: $LEGACY_QUD_DIR" + if [ -f "$LEGACY_QUD_DIR/QcDevDriver.sh" ]; then + echo " Running legacy uninstaller..." + bash "$LEGACY_QUD_DIR/QcDevDriver.sh" uninstall 2>/dev/null || true + fi + echo " Removing legacy directory: $LEGACY_QUD_DIR" + rm -rf "$LEGACY_QUD_DIR" + fi + + # Unload userspace-specific modules (GobiNet, QdssDiag) + for mod in $USERSPACE_MODULES; do + mod_under=$(echo "$mod" | tr '-' '_') + if lsmod | grep -qi "^${mod_under}"; then + USERSPACE_FOUND=true + echo " Unloading userspace module: $mod" + rmmod "$mod" 2>/dev/null || true + fi + done + + # Remove userspace-specific module files from kernel tree + KVER=$(uname -r) + for mod in GobiNet.ko QdssDiag.ko qdssdiag.ko; do + for dir in "/lib/modules/$KVER/kernel/drivers/net/usb" "/lib/modules/$KVER/kernel/drivers/usb/misc" "/lib/modules/$KVER/extra"; do + for ext in "" ".xz" ".zst" ".gz"; do + fpath="$dir/${mod}${ext}" + if [ -f "$fpath" ]; then + USERSPACE_FOUND=true + echo " Removing userspace module file: $fpath" + rm -f "$fpath" + fi + done + done + done + + # Stop and disable the userspace systemd service + if systemctl list-unit-files "$USERSPACE_SERVICE" >/dev/null 2>&1; then + if systemctl is-active "$USERSPACE_SERVICE" >/dev/null 2>&1; then + USERSPACE_FOUND=true + echo " Stopping userspace service: $USERSPACE_SERVICE" + systemctl stop "$USERSPACE_SERVICE" 2>/dev/null || true + fi + if systemctl is-enabled "$USERSPACE_SERVICE" >/dev/null 2>&1; then + echo " Disabling userspace service: $USERSPACE_SERVICE" + systemctl disable "$USERSPACE_SERVICE" 2>/dev/null || true + fi + # Remove the service file + rm -f "/etc/systemd/system/$USERSPACE_SERVICE" 2>/dev/null || true + systemctl daemon-reload 2>/dev/null || true + fi + + # Remove userspace config file + if [ -f "$USERSPACE_CONFIG" ]; then + echo " Removing userspace config: $USERSPACE_CONFIG" + rm -f "$USERSPACE_CONFIG" + fi + + if [ "$USERSPACE_FOUND" = true ]; then + echo " Userspace driver cleanup complete." + echo "" + fi + + # --- Step 1: Unload any currently loaded kernel-mode modules --- + for mod in qcom-serial qcom_usbnet qcom_usb qtiDevInf; do + mod_under=$(echo "$mod" | tr '-' '_') + if lsmod | grep -q "^${mod_under}"; then + echo " Unloading currently loaded module: $mod" + rmmod "$mod" 2>/dev/null || true + fi + done + + # --- Step 2: Remove any older DKMS versions of our package --- + if command -v dkms >/dev/null 2>&1; then + # Get all installed versions of our package + OLD_VERSIONS=$(dkms status "$PKG_NAME" 2>/dev/null | grep -oP "${PKG_NAME}/\K[^,]+" || true) + for old_ver in $OLD_VERSIONS; do + old_ver=$(echo "$old_ver" | tr -d '[:space:]') + if [ -n "$old_ver" ] && [ "$old_ver" != "$PKG_VERSION" ]; then + echo " Removing older DKMS version: $PKG_NAME/$old_ver" + dkms remove "$PKG_NAME/$old_ver" --all 2>/dev/null || true + fi + done + fi + + # --- Step 3: Remove manually installed modules from kernel tree --- + # These are modules installed outside of DKMS (e.g., via make install) + MANUAL_PATHS=( + "/lib/modules/$KVER/kernel/drivers/usb/misc" + "/lib/modules/$KVER/kernel/drivers/net/usb" + "/lib/modules/$KVER/kernel/drivers/usb/serial" + "/lib/modules/$KVER/extra" + ) + MODULE_FILES="qtiDevInf.ko qcom_usb.ko qcom_usbnet.ko qcom-serial.ko" + + for dir in "${MANUAL_PATHS[@]}"; do + for mf in $MODULE_FILES; do + # Check for .ko, .ko.xz, .ko.zst, .ko.gz variants + for ext in "" ".xz" ".zst" ".gz"; do + fpath="$dir/${mf}${ext}" + if [ -f "$fpath" ]; then + echo " Removing pre-existing module: $fpath" + rm -f "$fpath" + fi + done + done + done + + # Update module dependencies after removal + if [ -d "/lib/modules/$KVER" ]; then + depmod -a "$KVER" 2>/dev/null || true + fi + + echo " Pre-installation cleanup complete." + ;; +esac + +#DEBHELPER# + +exit 0 \ No newline at end of file diff --git a/debian/qcom-usb-drivers-dkms.prerm b/debian/qcom-usb-drivers-dkms.prerm new file mode 100755 index 0000000..5d00b82 --- /dev/null +++ b/debian/qcom-usb-drivers-dkms.prerm @@ -0,0 +1,30 @@ +#!/bin/bash +# Pre-removal script for qcom-usb-drivers-dkms +set -e + +case "$1" in + remove|upgrade|deconfigure) + # Unload Qualcomm modules (reverse dependency order: serial depends on qtiDevInf) + for mod in qcom-serial qcom_usbnet qcom_usb qtiDevInf; do + if lsmod | grep -qw "${mod//-/_}"; then + rmmod "$mod" 2>/dev/null || true + fi + done + + # Restore blacklisted in-tree modules + # (the blacklist config file is removed automatically with the package) + depmod -a 2>/dev/null || true + ;; + + failed-upgrade) + ;; + + *) + echo "prerm called with unknown argument '$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# + +exit 0 \ No newline at end of file diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..5c36c9c --- /dev/null +++ b/debian/rules @@ -0,0 +1,65 @@ +#!/usr/bin/make -f + +PKG_NAME := qcom-usb-drivers +PKG_VERSION := 1.0.6.1 +DKMS_SRC := debian/qcom-usb-drivers-dkms/usr/src/$(PKG_NAME)-$(PKG_VERSION) + +%: + dh $@ --with dkms + +override_dh_auto_configure: +override_dh_auto_build: +override_dh_auto_test: +override_dh_auto_install: + # Create DKMS source directory + mkdir -p $(DKMS_SRC) + + # Copy top-level Makefile (the one that builds all sub-modules) + cp src/linux/Makefile $(DKMS_SRC)/Makefile + + # Copy version header + cp src/linux/version.h $(DKMS_SRC)/version.h + + # Copy DKMS configuration + cp dkms.conf $(DKMS_SRC)/dkms.conf + + # Copy InfParser sources + mkdir -p $(DKMS_SRC)/InfParser + cp src/linux/InfParser/Makefile $(DKMS_SRC)/InfParser/ + cp src/linux/InfParser/*.c $(DKMS_SRC)/InfParser/ + cp src/linux/InfParser/*.h $(DKMS_SRC)/InfParser/ + + # Copy qcom_usb sources + mkdir -p $(DKMS_SRC)/qcom_usb + cp src/linux/qcom_usb/Makefile $(DKMS_SRC)/qcom_usb/ + cp src/linux/qcom_usb/*.c $(DKMS_SRC)/qcom_usb/ + cp src/linux/qcom_usb/*.h $(DKMS_SRC)/qcom_usb/ + + # Copy qcom_usbnet sources + mkdir -p $(DKMS_SRC)/qcom_usbnet + cp src/linux/qcom_usbnet/Makefile $(DKMS_SRC)/qcom_usbnet/ + cp src/linux/qcom_usbnet/*.c $(DKMS_SRC)/qcom_usbnet/ + cp src/linux/qcom_usbnet/*.h $(DKMS_SRC)/qcom_usbnet/ + + # Copy qcom_serial sources + mkdir -p $(DKMS_SRC)/qcom_serial + cp src/linux/qcom_serial/Makefile $(DKMS_SRC)/qcom_serial/ + cp src/linux/qcom_serial/*.c $(DKMS_SRC)/qcom_serial/ + cp src/linux/qcom_serial/*.h $(DKMS_SRC)/qcom_serial/ + + # Copy INF files needed by the drivers + cp src/linux/qcom_usb/*.inf $(DKMS_SRC)/qcom_usb/ 2>/dev/null || true + cp src/linux/qcom_usbnet/*.inf $(DKMS_SRC)/qcom_usbnet/ 2>/dev/null || true + cp src/linux/qcom_serial/*.inf $(DKMS_SRC)/qcom_serial/ 2>/dev/null || true + + # Install udev rules (modprobe config is installed automatically by dh_dkms from .modprobe file) + mkdir -p debian/qcom-usb-drivers-dkms/etc/udev/rules.d + echo 'SUBSYSTEMS=="qcom_usbnet", MODE="0666"' > debian/qcom-usb-drivers-dkms/etc/udev/rules.d/99-qcom-usb-devices.rules + echo 'SUBSYSTEMS=="qcom_usb", MODE="0666"' >> debian/qcom-usb-drivers-dkms/etc/udev/rules.d/99-qcom-usb-devices.rules + echo 'SUBSYSTEMS=="qcom_ports", MODE="0666"' >> debian/qcom-usb-drivers-dkms/etc/udev/rules.d/99-qcom-usb-devices.rules + echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="05c6", NAME="usb%n"' > debian/qcom-usb-drivers-dkms/etc/udev/rules.d/80-qcom-usbnet-devices.rules + +override_dh_dkms: + dh_dkms -V $(PKG_VERSION) + +override_dh_auto_clean: \ No newline at end of file diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..9f67427 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) \ No newline at end of file diff --git a/dkms.conf b/dkms.conf new file mode 100644 index 0000000..435c066 --- /dev/null +++ b/dkms.conf @@ -0,0 +1,27 @@ +# DKMS configuration for Qualcomm USB kernel drivers +PACKAGE_NAME="qcom-usb-drivers" +PACKAGE_VERSION="1.0.6.1" + +# Module 1: qtiDevInf (INF Parser) +BUILT_MODULE_NAME[0]="qtiDevInf" +BUILT_MODULE_LOCATION[0]="InfParser/" +DEST_MODULE_LOCATION[0]="/updates/dkms" + +# Module 2: qcom_usb (USB diagnostic/QDSS driver) +BUILT_MODULE_NAME[1]="qcom_usb" +BUILT_MODULE_LOCATION[1]="qcom_usb/" +DEST_MODULE_LOCATION[1]="/updates/dkms" + +# Module 3: qcom_usbnet (USB network driver) +BUILT_MODULE_NAME[2]="qcom_usbnet" +BUILT_MODULE_LOCATION[2]="qcom_usbnet/" +DEST_MODULE_LOCATION[2]="/updates/dkms" + +# Module 4: qcom-serial (USB serial driver) +BUILT_MODULE_NAME[3]="qcom-serial" +BUILT_MODULE_LOCATION[3]="qcom_serial/" +DEST_MODULE_LOCATION[3]="/updates/dkms" + +AUTOINSTALL="yes" +CLEAN="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build clean" +MAKE[0]="make -C ${kernel_source_dir} M=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules" \ No newline at end of file diff --git a/src/linux/QcDevDriver.sh b/src/linux/QcDevDriver.sh new file mode 100644 index 0000000..767ed21 --- /dev/null +++ b/src/linux/QcDevDriver.sh @@ -0,0 +1,1083 @@ +#!/bin/bash +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause + +DEST_QTI_PATH=/opt/QTI/ +DEST_QUD_PATH=/opt/QTI/QUD +DEST_INS_RMNET_PATH=/opt/QTI/QUD/rmnet +DEST_INS_QDSS_PATH=/opt/QTI/QUD/diag +DEST_INF_PATH=/opt/QTI/QUD/diag/InfParser +DEST_QDSS_DAIG_PATH=/opt/QTI/QUD/diag/QdssDiag +DEST_SIGN_PATH=/opt/QTI/sign +QC_MODBIN_DIR=/sbin +QC_MAKE_DIR=/usr/bin +QC_MODULE_INF_NAME=qtiDevInf.ko +QC_MODULE_QDSS_DIAG_NAME=QdssDiag.ko +QC_MODULE_RMNET_NAME=GobiNet.ko +QC_DIAG_INF_PATH=/opt/QTI/QUD/diag/qtiser.inf +QC_QDSS_INF_PATH=/opt/QTI/QUD/diag/qdbusb.inf +QC_MODEM_INF_PATH=/opt/QTI/QUD/serial/qtimdm.inf +DEST_INS_SERIAL_PATH=/opt/QTI/QUD/serial +QC_UDEV_PATH=/etc/udev/rules.d +QC_MODULE_GOBISERIAL_NAME=GobiSerial.ko +QC_SERIAL=/lib/modules/`uname -r`/kernel/drivers/usb/serial +QC_QMI_WWAN=/lib/modules/`uname -r`/kernel/drivers/net/usb +QC_NET=/lib/modules/`uname -r`/kernel/drivers/net +QC_LN_RM_MK_DIR=/bin +MODULE_BLACKLIST=/etc/modprobe.d +OS_RELEASE="`cat /etc/os-release | grep PRETTY_NAME`" +OSName=`echo $OS_RELEASE | awk -F= '{printf $2}'` +KERNEL_VERSION=`uname -r` +GREEN='\033[0;32m' +RED='\033[0;31m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +RESET='\033[0m' + +#check and install mokutil package +if [ ! -f "$QC_MAKE_DIR/mokutil" ]; then + echo -e "${RED}Error: mokutil not found, installing..\n${RESET}" +fi + +if [ ! -f "$QC_MAKE_DIR/keyctl" ]; then + echo -e ${RED}"Error: keyutils not found, installing..\n"${RESET} +fi + +if [[ ! -f "$QC_MAKE_DIR/mokutil" ]] || [[ ! -f "$QC_MAKE_DIR/keyctl" ]]; then + if [[ $OSName =~ "Red Hat Enterprise Linux" ]]; then + sudo dnf install -y mokutil + sudo dnf install -y keyutils + fi + + if [[ $OSName =~ "Ubuntu" ]]; then + sudo apt-get install -y mokutil + sudo apt-get install -y keyutils + fi +fi + +if [[ $OSName =~ "Fedora Linux" ]]; then + sudo dnf install -y make automake gcc gcc-c++ kernel-devel +fi + +QC_SECURE_BOOT_CHECK=`mokutil --sb-state` + +if [[ $QC_SECURE_BOOT_CHECK = "SecureBoot enabled" ]]; then + QC_PUBLIC_KEY_VERIFY=`mokutil --test-key $DEST_SIGN_PATH/Signkey_pub.der` +fi + +if [ ! -d $DEST_QUD_PATH ]; then + echo -e "${RED}Error: $DEST_QUD_PATH doesn't exist. Creating Now.${RESET}" + $QC_LN_RM_MK_DIR/mkdir -m 0755 -p $DEST_QUD_PATH +fi + +if [ $# == 0 ]; then + echo -e "${RED}Usage: QCDevInstaller.sh ${RESET}" + exit 1 +else + if [ $1 == "uninstall" ]; then + echo -e "Operating System: $OSName" + echo -e "Kernel Version: "\""`uname -r`"\""" + + if [ -f ./version.h ]; then + VERSION="`grep -r '#define DRIVER_VERSION' version.h`" + DRIVER_VERSION=`echo $VERSION | awk '{printf $3}'` + echo -e "Driver Version: $DRIVER_VERSION" + elif [ -f ./BuildPackage/version.h ]; then + VERSION="`grep -r '#define DRIVER_VERSION' ./BuildPackage/version.h`" + DRIVER_VERSION=`echo $VERSION | awk '{printf $3}'` + echo -e "Driver Version: $DRIVER_VERSION" + fi + + if [ -d $DEST_INS_QDSS_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + if [ ! -d $DEST_INS_QDSS_PATH ]; then + echo -e "Successfully removed $DEST_INS_QDSS_PATH" + else + echo -e "${RED}Failed to remove $DEST_INS_QDSS_PATH${RESET}" + fi + else + echo -e "$DEST_INS_QDSS_PATH does not exist, nothing to remove" + fi + if [ -d $DEST_INS_RMNET_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + if [ ! -d $DEST_INS_RMNET_PATH ]; then + echo -e "Successfully removed $DEST_INS_RMNET_PATH" + else + echo -e "${RED}Failed to remove $DEST_INS_RMNET_PATH${RESET}" + fi + else + echo -e "$DEST_INS_RMNET_PATH does not exist, nothing to remove" + fi + if [ -d $DEST_INS_SERIAL_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_SERIAL_PATH + if [ ! -d $DEST_INS_SERIAL_PATH ]; then + echo -e "Successfully removed $DEST_INS_SERIAL_PATH" + else + echo -e "${RED}Failed to remove $DEST_INS_SERIAL_PATH${RESET}" + fi + else + echo -e "$DEST_INS_SERIAL_PATH does not exist, nothing to remove" + fi + + if [ -f $QC_UDEV_PATH/qti_usb_device.rules ]; then + rm -r $QC_UDEV_PATH/qti_usb_device.rules + if [ ! -f $QC_UDEV_PATH/qti_usb_device.rules ]; then + echo -e "Successfully removed $QC_UDEV_PATH/qti_usb_device.rules" + else + echo -e "${RED}Failed to remove $QC_UDEV_PATH/qti_usb_device.rules${RESET}" + fi + else + echo -e "$QC_UDEV_PATH/qti_usb_device.rules does not exist, nothing to remove" + fi + # < "Redundant 80-net-setup-link.rules. This code will be eliminated after few releases" + if [ -f $QC_UDEV_PATH/80-net-setup-link.rules ]; then + sed -i '/GobiQMI/d' $QC_UDEV_PATH/80-net-setup-link.rules + if [ ! -s $QC_UDEV_PATH/80-net-setup-link.rules ]; then + echo -e "Removed GobiQMI rule from $QC_UDEV_PATH/80-net-setup-link.rules" + rm -rf $QC_UDEV_PATH/80-net-setup-link.rules + if [ ! -f $QC_UDEV_PATH/80-net-setup-link.rules ]; then + echo -e "File was empty. Removed $QC_UDEV_PATH/80-net-setup-link.rules successfully" + else + echo -e "File is empty but Failed to remove $QC_UDEV_PATH/80-net-setup-link.rules" + fi + else + echo -e "Pre-Existing data $QC_UDEV_PATH/80-net-setup-link.rules, so not removing it." + fi + else + echo -e "$QC_UDEV_PATH/80-net-setup-link.rules does not exist, nothing to remove" + fi + # > + if [ -f $QC_UDEV_PATH/80-gobinet-usbdevice.rules ]; then + sed -i '/usb/d' $QC_UDEV_PATH/80-gobinet-usbdevice.rules + if [ ! -s $QC_UDEV_PATH/80-gobinet-usbdevice.rules ]; then + echo -e "Removed GobiQMI rule from $QC_UDEV_PATH/80-gobinet-usbdevice.rules" + rm -rf $QC_UDEV_PATH/80-gobinet-usbdevice.rules + if [ ! -f $QC_UDEV_PATH/80-gobinet-usbdevice.rules ]; then + echo -e "File was empty. Removed $QC_UDEV_PATH/80-gobinet-usbdevice.rules successfully" + else + echo -e "File is empty but Failed to remove $QC_UDEV_PATH/80-gobinet-usbdevice.rules" + fi + else + echo -e "Pre-Existing data $QC_UDEV_PATH/80-gobinet-usbdevice.rules, so not removing it." + fi + else + echo -e "$QC_UDEV_PATH/80-gobinet-usbdevice.rules does not exist, nothing to remove" + fi + + # Informs udev deamon to reload the newly added device rule and re-trigger service + sudo udevadm control --reload-rules + sudo udevadm trigger + + if [ "`lsmod | grep GobiSerial`" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_GOBISERIAL_NAME && echo -e "$QC_MODULE_GOBISERIAL_NAME removed successfully") || { echo -e "$QC_MODULE_GOBISERIAL_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } + else + echo -e "Module $QC_MODULE_GOBISERIAL_NAME is not currently loaded" + fi + if [ "`lsmod | grep GobiNet`" ]; then + ( $QC_MODBIN_DIR/rmmod $QC_MODULE_RMNET_NAME && echo -e "$QC_MODULE_RMNET_NAME removed successfully" ) || { echo -e "$QC_MODULE_RMNET_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } + else + echo -e "Module $QC_MODULE_RMNET_NAME is not currently loaded" + fi + if [ "`lsmod | grep QdssDiag`" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_QDSS_DIAG_NAME && echo -e "$QC_MODULE_QDSS_DIAG_NAME removed successfully") || { echo -e "$QC_MODULE_QDSS_DIAG_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } + else + echo -e "Module $QC_MODULE_QDSS_DIAG_NAME is not currently loaded" + fi + if [ "`lsmod | grep qtiDevInf`" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_INF_NAME && echo -e "$QC_MODULE_INF_NAME removed successfully") || { echo -e "$QC_MODULE_INF_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } + else + echo -e "Module $QC_MODULE_INF_NAME is not currently loaded" + fi + + MODLOADED="`/sbin/lsmod | grep usb_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "usb_wwan module is already loaded. nothing to do" + fi + if [ -f $QC_SERIAL/usb_wwan_dup* ]; then + echo -e "usb_wwan_dup is found. restoring to usb_wwan" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/usb_wwan_dup* /lib/modules/`uname -r`/kernel/drivers/usb/serial/usb_wwan.ko + #$QC_MODBIN_DIR/insmod /lib/modules/`uname -r`/kernel/drivers/usb/serial/usb_wwan.ko + + MODLOADED="`/sbin/lsmod | grep usb_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "Successfully loaded usb_wwan module." + fi + fi + + MODLOADED="`/sbin/lsmod | grep qcserial`" + if [ "$MODLOADED" != "" ]; then + echo -e "qcserial module is already loaded. nothing to do" + fi + if [ -f $QC_SERIAL/qcserial_dup* ]; then + echo -e "qcserial_dup is found. restoring to qcserial" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/qcserial_dup* /lib/modules/`uname -r`/kernel/drivers/usb/serial/qcserial.ko + #$QC_MODBIN_DIR/insmod /lib/modules/`uname -r`/kernel/drivers/usb/serial/qcserial.ko + + MODLOADED="`/sbin/lsmod | grep qcserial`" + if [ "$MODLOADED" != "" ]; then + echo -e "Successfully loaded qcserial module." + fi + fi + + MODLOADED="`/sbin/lsmod | grep option`" + if [ "$MODLOADED" != "" ]; then + echo -e "option module is already loaded. nothing to do" + fi + if [ -f $QC_SERIAL/option_dup* ]; then + echo -e "option_dup is found. restoring to option" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/option_dup* /lib/modules/`uname -r`/kernel/drivers/usb/serial/option.ko + #$QC_MODBIN_DIR/insmod /lib/modules/`uname -r`/kernel/drivers/usb/serial/option.ko + + MODLOADED="`/sbin/lsmod | grep option`" + if [ "$MODLOADED" != "" ]; then + echo -e "Successfully loaded option module." + fi + fi + + MODLOADED="`/sbin/lsmod | grep qmi_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "qmi_wwan module is already loaded. nothing to do" + fi + if [ -f $QC_QMI_WWAN/qmi_wwan_dup* ]; then + echo -e "qmi_wwan_dup is found. restoring to qmi_wwan" + mv /lib/modules/`uname -r`/kernel/drivers/usb/class/cdc-wdm_dup* /lib/modules/`uname -r`/kernel/drivers/usb/class/cdc-wdm.ko + mv /lib/modules/`uname -r`/kernel/drivers/net/usb/qmi_wwan_dup* /lib/modules/`uname -r`/kernel/drivers/net/usb/qmi_wwan.ko + #$QC_MODBIN_DIR/insmod /lib/modules/`uname -r`/kernel/drivers/usb/class/cdc-wdm.ko + #$QC_MODBIN_DIR/insmod /lib/modules/`uname -r`/kernel/drivers/net/usb/qmi_wwan.ko + + MODLOADED="`/sbin/lsmod | grep qmi_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "Successfully loaded qmi_wwan module." + fi + fi + + if [[ $OSName =~ "Ubuntu 24.04" ]]; then + if [ -f $QC_NET/mii.ko ]; then + $QC_LN_RM_MK_DIR/rm -rf $QC_NET/mii.ko + fi + if [ -f $QC_QMI_WWAN/usbnet.ko ]; then + $QC_LN_RM_MK_DIR/rm -rf $QC_QMI_WWAN/usbnet.ko + fi + fi + + if [ "`grep -nr 'Qualcomm clients' /etc/modprobe.d/blacklist.conf`" != "" ]; then + sed -i '/# Blacklist these module so that Qualcomm clients use only/d' /etc/modprobe.d/blacklist.conf + sed -i '/# GobiNet, GobiSerial, QdssDiag, qtiDevInf driver/d' /etc/modprobe.d/blacklist.conf + fi + + MOD_EXIST="`grep -nr 'blacklist qcserial' /etc/modprobe.d/blacklist.conf`" + if [ "$MOD_EXIST" != "" ]; then + sed -i '/qcserial/d' $MODULE_BLACKLIST/blacklist.conf + echo -e "Successfully removed qcserial from $MODULE_BLACKLIST/blacklist.conf" + fi + + MOD_EXIST="`grep -nr 'blacklist qmi_wwan' /etc/modprobe.d/blacklist.conf`" + if [ "$MOD_EXIST" != "" ]; then + sed -i '/qmi_wwan/d' $MODULE_BLACKLIST/blacklist.conf + echo -e "Successfully removed qmi_wwan from $MODULE_BLACKLIST/blacklist.conf" + fi + + MOD_EXIST="`grep -nr 'blacklist option' /etc/modprobe.d/blacklist.conf`" + if [ "$MOD_EXIST" != "" ]; then + sed -i '/option/d' $MODULE_BLACKLIST/blacklist.conf + echo -e "Successfully removed option from $MODULE_BLACKLIST/blacklist.conf" + fi + + MOD_EXIST="`grep -nr 'blacklist usb_wwan' /etc/modprobe.d/blacklist.conf`" + if [ "$MOD_EXIST" != "" ]; then + sed -i '/usb_wwan/d' $MODULE_BLACKLIST/blacklist.conf + echo -e "Successfully removed usb_wwan from $MODULE_BLACKLIST/blacklist.conf" + fi + + #change to permission to default mode + $QC_LN_RM_MK_DIR/chmod 644 $MODULE_BLACKLIST/blacklist.conf + + echo -e "Removing modules for /etc/modules." + MODUPDATE="`grep -r qtiDevInf /etc/modules`" + if [ "$MODUPDATE" == "qtiDevInf" ]; then + sed -i '/qtiDevInf/d' /etc/modules + fi + MODUPDATE="`grep -r QdssDiag /etc/modules`" + if [ "$MODUPDATE" == "QdssDiag" ]; then + sed -i '/QdssDiag/d' /etc/modules + fi + MODUPDATE="`grep -r GobiNet /etc/modules`" + if [ "$MODUPDATE" == "GobiNet" ]; then + sed -i '/GobiNet/d' /etc/modules + fi + MODUPDATE="`grep -r GobiSerial /etc/modules`" + if [ "$MODUPDATE" == "GobiSerial" ]; then + sed -i '/GobiSerial/d' /etc/modules + fi + if [[ $OSName != *"Red Hat Enterprise Linux"* ]]; then + MODUPDATE="`grep -nr 'iface usb0 inet static' /etc/network/interfaces`" + if [ "$MODUPDATE" != "" ]; then + sed -i '/iface usb0 inet static/d' /etc/network/interfaces + fi + fi + + echo -e "Removing modules from $QC_SERIAL" + if [ -f $QC_SERIAL/$QC_MODULE_GOBISERIAL_NAME ]; then + rm -rf $QC_SERIAL/$QC_MODULE_GOBISERIAL_NAME + fi + echo -e "Removing modules from $QC_QMI_WWAN" + if [ -f $QC_QMI_WWAN/$QC_MODULE_QDSS_DIAG_NAME ]; then + rm -rf $QC_QMI_WWAN/$QC_MODULE_QDSS_DIAG_NAME + fi + if [ -f $QC_QMI_WWAN/$QC_MODULE_RMNET_NAME ]; then + rm -rf $QC_QMI_WWAN/$QC_MODULE_RMNET_NAME + fi + if [ -f $QC_QMI_WWAN/$QC_MODULE_INF_NAME ]; then + rm -rf $QC_QMI_WWAN/$QC_MODULE_INF_NAME + fi + # update modules.dep and modules.alias + depmod + + echo -e "Uninstallation completed successfully." + exit 0 + else + if [ $1 != "install" ]; then + echo -e "Usage: QCDevInstaller.sh " + exit 1 + fi + fi +fi + +######## Installation ########### + +if [[ $OSName =~ "Ubuntu" ]]; then + sudo apt-get update + sudo apt-get install -y build-essential + sudo apt-get install -y gawk + sudo apt-get install -y python3-tk +fi + +IFS=. read -r major_ver minor_ver patch_ver <<< "$KERNEL_VERSION" +if [[ $OSName =~ "Ubuntu 22." ]] && (( "$major_ver" >= 6 && "$minor_ver" >= 5 )); then + echo -e "Installing gcc 12 version ..." + sudo apt install -y gcc-12 g++-12 +fi + +echo -e "${CYAN}=======================================================================================" +echo -e "=======================================================================================${RESET}" +echo -e " " + +echo -e "Operating System:${RED} $OSName"${RESET} +echo -e "Kernel Version: ${RED}"\"$KERNEL_VERSION\"""${RESET} + +if [ -f ./version.h ]; then + VERSION="`grep -r '#define DRIVER_VERSION' version.h`" + DRIVER_VERSION=`echo $VERSION | awk '{printf $3}'` + echo -e "Driver Version: $DRIVER_VERSION" +fi + +echo -e "Installing at the following paths:" +echo $DEST_INS_SERIAL_PATH +echo $DEST_INS_QDSS_PATH +echo $DEST_INS_RMNET_PATH + +# this script will use in qik uninstallation process +if [ -f "$DEST_QUD_PATH/QcDevDriver.sh" ]; then + echo -e "Delete and copy again (QcDevDriver.sh)" + $QC_LN_RM_MK_DIR/rm -rf $DEST_QUD_PATH/QcDevDriver.sh + $QC_LN_RM_MK_DIR/cp -rf ./QcDevDriver.sh $DEST_QUD_PATH/ +else + echo -e "Does not exist and copying now.. (QcDevDriver.sh)" + $QC_LN_RM_MK_DIR/cp -rf ./QcDevDriver.sh $DEST_QUD_PATH/ +fi + +# Create directories +if [ -d $DEST_INS_SERIAL_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_SERIAL_PATH +fi + +$QC_LN_RM_MK_DIR/mkdir -m 0755 -p $DEST_INS_SERIAL_PATH +if [ ! -d $DEST_INS_SERIAL_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +if [ -d $DEST_INS_QDSS_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH +fi + +$QC_LN_RM_MK_DIR/mkdir -m 0755 -p $DEST_INS_QDSS_PATH +if [ ! -d $DEST_INS_QDSS_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +if [ -d $DEST_INF_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INF_PATH +fi + +$QC_LN_RM_MK_DIR/mkdir -m 0755 -p $DEST_INF_PATH +if [ ! -d $DEST_INF_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +if [ -d $DEST_QDSS_DAIG_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_QDSS_DAIG_PATH +fi + +$QC_LN_RM_MK_DIR/mkdir -m 0755 -p $DEST_QDSS_DAIG_PATH +if [ ! -d $DEST_QDSS_DAIG_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +# Important: Do not delete or recreate the "sign" folder. +# The sign files will be automatically generated whenever the Signpub key is not enrolled. +$QC_LN_RM_MK_DIR/mkdir -m 0777 -p $DEST_SIGN_PATH +if [ ! -d $DEST_SIGN_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +# $QC_LN_RM_MK_DIR/cp ./GobiSerial/GobiSerial.c $DEST_INS_SERIAL_PATH +# if [ ! -f $DEST_INS_SERIAL_PATH/GobiSerial.c ]; then +# echo -e "${RED}Error: Failed to copy GobiSerial.c to installation path, installation abort." +# rm -r $DEST_INS_SERIAL_PATH +# exit 1 +# fi + +# $QC_LN_RM_MK_DIR/cp ./GobiSerial/GobiSerial.h $DEST_INS_SERIAL_PATH +# if [ ! -f $DEST_INS_SERIAL_PATH/GobiSerial.h ]; then +# echo -e "${RED}Error: Failed to copy GobiSerial.h to installation path, installation abort." +# rm -r $DEST_INS_SERIAL_PATH +# exit 1 +# fi + +# $QC_LN_RM_MK_DIR/cp ./GobiSerial/Makefile $DEST_INS_SERIAL_PATH +# if [ ! -f $DEST_INS_SERIAL_PATH/Makefile ]; then +# echo -e "${RED}Error: Failed to copy Makefile installation path, installation abort." +# rm -r $DEST_INS_SERIAL_PATH +# exit 1 +# fi + +# $QC_LN_RM_MK_DIR/cp ./GobiSerial/qtidev.pl $DEST_INS_SERIAL_PATH +# if [ ! -f $DEST_INS_SERIAL_PATH/qtidev.pl ]; then +# echo -e "${RED}Error: Failed to copy qtidev.pl installation path, installation abort." +# rm -r $DEST_INS_SERIAL_PATH +# exit 1 +# fi +# $QC_LN_RM_MK_DIR/chmod 755 $DEST_INS_SERIAL_PATH/qtidev.pl + +$QC_LN_RM_MK_DIR/cp ./GobiSerial/qtiname.inf $DEST_INS_SERIAL_PATH +if [ ! -f $DEST_INS_SERIAL_PATH/qtiname.inf ]; then + echo -e "${RED}Error: Failed to copy qtiname.inf installation path, installation abort." + rm -r $DEST_INS_SERIAL_PATH + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_SERIAL_PATH/qtiname.inf + +$QC_LN_RM_MK_DIR/cp ./GobiSerial/qtimdm.inf $DEST_INS_SERIAL_PATH +if [ ! -f $DEST_INS_SERIAL_PATH/qtimdm.inf ]; then + echo -e "${RED}Error: Failed to copy qtiname.inf installation path, installation abort." + rm -r $DEST_INS_SERIAL_PATH + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_SERIAL_PATH/qtimdm.inf + + +$QC_LN_RM_MK_DIR/cp ./GobiSerial/qtiname.inf $DEST_INS_QDSS_PATH/ +if [ ! -f $DEST_INS_QDSS_PATH//qtiname.inf ]; then + echo -e "${RED}Error: Failed to copy qtiname.inf installation path, installation abort." + rm -r $DEST_INS_QDSS_PATH/ + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_SERIAL_PATH/qtiname.inf + +$QC_LN_RM_MK_DIR/cp ./GobiSerial/qtimdm.inf $DEST_INS_QDSS_PATH/ +if [ ! -f $DEST_INS_QDSS_PATH//qtimdm.inf ]; then + echo -e "${RED}Error: Failed to copy qtiname.inf installation path, installation abort." + rm -r $DEST_INS_QDSS_PATH/ + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_QDSS_PATH/qtimdm.inf + + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/qdbusb.inf $DEST_INS_QDSS_PATH/ +if [ ! -f $DEST_INS_QDSS_PATH/qdbusb.inf ]; then + echo -e "${RED}Error: Failed to copy 'qdbusb.inf' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/qtiser.inf $DEST_INS_QDSS_PATH/ +if [ ! -f $DEST_INS_QDSS_PATH/qtiser.inf ]; then + echo -e "${RED}Error: Failed to copy 'qtiser.inf' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_QDSS_PATH/qdbusb.inf +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_QDSS_PATH/qtiser.inf + +$QC_LN_RM_MK_DIR/cp ./InfParser/qtiDevInf.h $DEST_INF_PATH/ +if [ ! -f $DEST_INF_PATH/qtiDevInf.h ]; then + echo -e "${RED}Error: Failed to copy 'InfParser/qtiDevInf.h' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./InfParser/qtiDevInf.c $DEST_INF_PATH/ +if [ ! -f $DEST_INF_PATH/qtiDevInf.c ]; then + echo -e "${RED}Error: Failed to copy 'InfParser/qtiDevInf.c' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./InfParser/Makefile $DEST_INF_PATH/ +if [ ! -f $DEST_INF_PATH/Makefile ]; then + echo -e "${RED}Error: Failed to copy 'InfParser/Makefile' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/qtiDevInf.h $DEST_QDSS_DAIG_PATH/ +if [ ! -f $DEST_QDSS_DAIG_PATH/qtiDevInf.h ]; then + echo -e "${RED}Error: Failed to copy 'QdssDiag/qtiDevInf.h' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/qtiDiag.h $DEST_QDSS_DAIG_PATH/ +if [ ! -f $DEST_QDSS_DAIG_PATH/qtiDiag.h ]; then + echo -e "${RED}Error: Failed to copy 'QdssDiag/qtiDiag.h' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/qtiDiag.c $DEST_QDSS_DAIG_PATH/ +if [ ! -f $DEST_QDSS_DAIG_PATH/qtiDiag.c ]; then + echo -e "${RED}Error: Failed to copy 'QdssDiag/qtiDiag.c' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./Makefile $DEST_QDSS_DAIG_PATH/ +if [ ! -f $DEST_QDSS_DAIG_PATH/Makefile ]; then + echo -e "${RED}Error: Failed to copy 'QdssDiag/Makefile' to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + exit 1 +fi + +if [ -d $DEST_INS_RMNET_PATH ]; then + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH +fi + +$QC_LN_RM_MK_DIR/mkdir -p $DEST_INS_RMNET_PATH +if [ ! -d $DEST_INS_RMNET_PATH ]; then + echo -e "${RED}Error: Failed to create installation path, please run installer under root." + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/IPAssignmentScript.sh $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/IPAssignmentScript.sh ]; then + echo -e "${RED}Error: Failed to copy IPAssignmentScript.sh to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/chmod 755 $DEST_INS_RMNET_PATH/IPAssignmentScript.sh + +$QC_LN_RM_MK_DIR/cp ./rmnet/GobiUSBNet.c $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/GobiUSBNet.c ]; then + echo -e "${RED}Error: Failed to copy GobiUSBNet.c to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/QMIDevice.c $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/QMIDevice.c ]; then + echo -e "${RED}Error: Failed to copy QMIDevice.c to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/QMIDevice.h $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/QMIDevice.h ]; then + echo -e "${RED}Error: Failed to copy QMIDevice.h to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/QMI.c $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/QMI.c ]; then + echo -e "${RED}Error: Failed to copy QMI.c to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/QMI.h $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/QMI.h ]; then + echo -e "${RED}Error: Failed to copy QMI.h to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/qmap.c $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/qmap.c ]; then + echo -e "${RED}Error: Failed to copy qmap.c to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/qmap.h $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/qmap.h ]; then + echo -e "${RED}Error: Failed to copy qmap.h to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/Structs.h $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/Structs.h ]; then + echo -e "${RED}Error: Failed to copy Structs.h to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/Makefile $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/Makefile ]; then + echo -e "${RED}Error: Failed to copy Makefile installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/qtiwwan.inf $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/qtiwwan.inf ]; then + echo -e "${RED}Error: Failed to copy qtiwwan.inf installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_INS_RMNET_PATH/qtiwwan.inf + +#DEST_SIGN_PATH=/opt/QTI/sign/ +$QC_LN_RM_MK_DIR/cp ./sign/SignConf.config $DEST_SIGN_PATH +$QC_LN_RM_MK_DIR/chmod 777 $DEST_SIGN_PATH/SignConf.config +awk -i inplace -v name=`hostname` '{gsub(/O = /,"O = "name)}1' $DEST_SIGN_PATH/SignConf.config +awk -i inplace -v name=`hostname` '{gsub(/CN = /,"CN = "name" Signing Key")}1' $DEST_SIGN_PATH/SignConf.config +awk -i inplace -v name=`hostname` '{gsub(/emailAddress = /,"emailAddress = "name"@no-reply.com")}1' $DEST_SIGN_PATH/SignConf.config + +if [ ! -f $DEST_SIGN_PATH/SignConf.config ]; then + echo -e "${RED}Error: Failed to copy SignConf.config installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_SIGN_PATH + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./sign/signReadme.txt $DEST_SIGN_PATH +if [ ! -f $DEST_SIGN_PATH/signReadme.txt ]; then + echo -e "${RED}Error: Failed to copy signReadme.txt installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_SIGN_PATH + exit 1 +fi +$QC_LN_RM_MK_DIR/chmod 644 $DEST_SIGN_PATH/signReadme.txt + + +if [[ $QC_SECURE_BOOT_CHECK = "SecureBoot enabled" ]]; then + echo -e "${RED}SecureBoot enabled" + if [ -f $DEST_SIGN_PATH/Signkey_pub.der ]; then + if [[ $QC_PUBLIC_KEY_VERIFY = "$DEST_SIGN_PATH/Signkey_pub.der is already enrolled" ]]; then + echo -e "${CYAN}===========================================================" + echo -e "${GREEN}$DEST_SIGN_PATH/Signkey_pub.der is enrolled" + echo -e "${CYAN}===========================================================${RESET}" + else + echo -e "${RED}===========================================================" + echo -e "${CYAN}Secure Boot is enabled. User must enroll the Public Signkey located at /opt/QTI/sign/Signkey_pub.der" + echo -e "${CYAN}Please follow the mandatory instructions in /opt/QTI/sign/signReadme.txt" + echo -e "${CYAN}The QUD driver installation Failed!!!" + echo -e "${RED}===========================================================${RESET}" + exit 1 + fi + else + echo -e "${RED}Signkey_pub.der doesn't exist. Creating public and private key${RESET}" + openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 -batch -config $DEST_SIGN_PATH/SignConf.config -outform DER -out $DEST_SIGN_PATH/Signkey_pub.der -keyout $DEST_SIGN_PATH/Signkey.priv + $QC_LN_RM_MK_DIR/chmod 755 $DEST_SIGN_PATH/Signkey_pub.der + $QC_LN_RM_MK_DIR/chmod 755 $DEST_SIGN_PATH/Signkey.priv + echo -e "${RED}##############################################################" + echo -e "${CYAN}Secure Boot is enabled. User must enroll the Public Signkey located at /opt/QTI/sign/Signkey_pub.der" + echo -e "${CYAN}Please follow the mandatory instructions in /opt/QTI/sign/signReadme.txt" + echo -e "${CYAN}The QUD driver installation Failed!!!" + echo -e "${RED}##############################################################${RESET}" + exit 1 + fi +else + echo -e "${GREEN}Secure boot disabled${RESET}" +fi + +# "********************* Compilation of modules Starts ***************************" +$QC_MAKE_DIR/make install +if [ ! -f ./InfParser/$QC_MODULE_INF_NAME ]; then + echo -e "${RED}Error: Failed to generate kernel module $QC_MODULE_INF_NAME, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + $QC_MAKE_DIR/make clean + exit 1 +fi +if [ ! -f ./QdssDiag/$QC_MODULE_QDSS_DIAG_NAME ]; then + echo -e "${RED}Error: Failed to generate kernel module $QC_MODULE_QDSS_DIAG_NAME, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + $QC_MAKE_DIR/make clean + exit 1 +fi + +if [ ! -f ./rmnet/$QC_MODULE_RMNET_NAME ]; then + echo -e "${RED}Error: Failed to generate kernel module $QC_MODULE_RMNET_NAME, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + $QC_MAKE_DIR/make clean + exit 1 +fi + +# if [ ! -f ./GobiSerial/$QC_MODULE_GOBISERIAL_NAME ]; then +# echo -e "${RED}Error: Failed to generate kernel module $QC_MODULE_GOBISERIAL_NAME, installation abort." +# $QC_LN_RM_MK_DIR/rm -rf $QC_MODULE_GOBISERIAL_NAME +# $QC_MAKE_DIR/make clean +# exit 1 +# fi + +# Copy .ko object to destination folders +# $QC_LN_RM_MK_DIR/cp ./GobiSerial/$QC_MODULE_GOBISERIAL_NAME $DEST_INS_SERIAL_PATH +# if [ ! -f $DEST_INS_SERIAL_PATH/$QC_MODULE_GOBISERIAL_NAME ]; then +# echo -e "${RED}Error: Failed to copy $QC_MODULE_GOBISERIAL_NAME to installation path, installation abort." +# $QC_LN_RM_MK_DIR/rm -r $DEST_INS_SERIAL_PATH +# $QC_MAKE_DIR/make clean +# exit 1 +# fi + +$QC_LN_RM_MK_DIR/cp ./InfParser/$QC_MODULE_INF_NAME $DEST_INF_PATH +if [ ! -f $DEST_INF_PATH/$QC_MODULE_INF_NAME ]; then + echo -e "${RED}Error: Failed to copy $QC_MODULE_INF_NAME to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + $QC_MAKE_DIR/make clean + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./QdssDiag/$QC_MODULE_QDSS_DIAG_NAME $DEST_QDSS_DAIG_PATH +if [ ! -f $DEST_QDSS_DAIG_PATH/$QC_MODULE_QDSS_DIAG_NAME ]; then + echo -e "${RED}Error: Failed to copy $QC_MODULE_QDSS_DIAG_NAME to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_QDSS_PATH + $QC_MAKE_DIR/make clean + exit 1 +fi + +$QC_LN_RM_MK_DIR/cp ./rmnet/$QC_MODULE_RMNET_NAME $DEST_INS_RMNET_PATH +if [ ! -f $DEST_INS_RMNET_PATH/$QC_MODULE_RMNET_NAME ]; then + echo -e "${RED}Error: Failed to copy $QC_MODULE_RMNET_NAME to installation path, installation abort." + $QC_LN_RM_MK_DIR/rm -rf $DEST_INS_RMNET_PATH + exit 1 +fi + +$QC_MAKE_DIR/make clean + +# echo SUBSYSTEMS==\"tty\", PROGRAM=\"$DEST_INS_SERIAL_PATH/qtidev.pl $DEST_INS_SERIAL_PATH/qtiname.inf %k\", SYMLINK+=\"%c\" , MODE=\"0666\" > ./qti_usb_device.rules +echo SUBSYSTEMS==\"GobiQMI\", MODE=\"0666\" >> ./qti_usb_device.rules +echo SUBSYSTEMS==\"GobiUSB\", MODE=\"0666\" >> ./qti_usb_device.rules +echo SUBSYSTEMS==\"GobiPorts\", MODE=\"0666\" >> ./qti_usb_device.rules + +$QC_LN_RM_MK_DIR/chmod 644 ./qti_usb_device.rules +$QC_LN_RM_MK_DIR/cp ./qti_usb_device.rules $QC_UDEV_PATH +echo -e "Generated QC rules" + +# udev rules for QMI +if [ -f $QC_UDEV_PATH/80-gobinet-usbdevice.rules ]; then + RULE_EXIST="`grep -nr 'usb' $QC_UDEV_PATH/80-gobinet-usbdevice.rules`" + if [ "$RULE_EXIST" != "" ]; then + echo -e "Subsystem GobiQMI rule already exist in $QC_UDEV_PATH/80-gobinet-usbdevice.rules, nothing to add" + else + echo -e "Subsystem GobiQMI rule doesn't exist in $QC_UDEV_PATH/80-gobinet-usbdevice.rules, so adding now" + $QC_LN_RM_MK_DIR/chmod 644 $QC_UDEV_PATH/80-gobinet-usbdevice.rules + echo SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"05c6\", NAME=\"usb%n\" >> $QC_UDEV_PATH/80-gobinet-usbdevice.rules + fi +else + echo SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"05c6\", NAME=\"usb%n\" >> ./80-gobinet-usbdevice.rules + $QC_LN_RM_MK_DIR/chmod 644 ./80-gobinet-usbdevice.rules + $QC_LN_RM_MK_DIR/cp ./80-gobinet-usbdevice.rules $QC_UDEV_PATH + echo -e "Creating new udev rule for GobiQMI in $QC_UDEV_PATH/80-gobinet-usbdevice.rules" +fi + +# Informs udev deamon to reload the newly added device rule and re-trigger service +sudo udevadm control --reload-rules +sudo udevadm trigger + +if [ ! -f $QC_UDEV_PATH/qti_usb_device.rules ]; then + echo -e "${RED}Error: Failed to generate $QC_UDEV_PATH/qti_usb_device.rules" + exit 1 +fi + +if [ ! -f $QC_UDEV_PATH/80-gobinet-usbdevice.rules ]; then + echo -e "${RED}Error: Failed to generate $QC_UDEV_PATH/80-gobinet-usbdevice.rules" + exit 1 +fi + +rm -f ./qti_usb_device.rules +rm -f ./80-net-setup-link.rules +rm -f ./80-gobinet-usbdevice.rules +echo -e "Removed local rules" + +MODLOADED="`/sbin/lsmod | grep usbserial`" +if [ "$MODLOADED" == "" ]; then + echo -e "To load dependency" + echo -e "Loading module usbserial" + if [[ $OSName =~ "Red Hat Enterprise Linux" ]]; then + if [ -f $QC_SERIAL/usbserial.ko.xz ]; then + xz -d $QC_SERIAL/usbserial.ko.xz + $QC_MODBIN_DIR/insmod $QC_SERIAL/usbserial.ko + fi + MODLOADED="`/sbin/lsmod | grep usbserial`" + if [ "$MODLOADED" == "" ]; then + echo -e "$OSName: usbserial.ko module not present at $QC_SERIAL" + fi + else + $QC_MODBIN_DIR/insmod $QC_SERIAL/usbserial.ko + fi +else + echo -e "Module usbserial already in place" +fi + +#echo Changing Permission of blacklist file +$QC_LN_RM_MK_DIR/chmod 777 $MODULE_BLACKLIST/blacklist.conf +echo -e "Changed Permission of blacklist file" +if [ "`grep -nr 'Qualcomm clients' /etc/modprobe.d/blacklist.conf`" != "" ]; then + sed -i '/# Blacklist these module so that Qualcomm clients use only/d' /etc/modprobe.d/blacklist.conf + sed -i '/# GobiNet, GobiSerial, QdssDiag, qtiDevInf driver/d' /etc/modprobe.d/blacklist.conf +fi +echo -e "# Blacklist these module so that Qualcomm clients use only" >> /etc/modprobe.d/blacklist.conf +echo -e "# GobiNet, GobiSerial, QdssDiag, qtiDevInf driver" >> /etc/modprobe.d/blacklist.conf + +MOD_EXIST="`grep -nr 'blacklist qcserial' /etc/modprobe.d/blacklist.conf`" +if [ "$MOD_EXIST" != "" ]; then + sed -i '/qcserial/d' $MODULE_BLACKLIST/blacklist.conf +fi +echo -e "blacklist qcserial" >> /etc/modprobe.d/blacklist.conf +echo -e "install qcserial /bin/false" >> /etc/modprobe.d/blacklist.conf +echo -e "blacklisted qcserial module" + +MODLOADED="`/sbin/lsmod | grep qcserial`" +if [ "$MODLOADED" != "" ]; then + echo -e "qcserial is found. Unloaded qcserial module" + $QC_MODBIN_DIR/rmmod qcserial.ko + MODLOADED="`/sbin/lsmod | grep qcserial`" + if [ "$MODLOADED" != "" ]; then + echo -e "${RED}Failed to unload qcserial.ko. try manually sudo rmmod ModuleName${RESET}" + fi +fi +if [ -f $QC_SERIAL/qcserial.ko ]; then + echo -e "qcserial is found. renamed to qcserial_dup" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/qcserial.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial/qcserial_dup +fi + +MOD_EXIST="`grep -nr 'blacklist qmi_wwan' /etc/modprobe.d/blacklist.conf`" +if [ "$MOD_EXIST" != "" ]; then + sed -i '/qmi_wwan/d' $MODULE_BLACKLIST/blacklist.conf +fi +echo -e "blacklist qmi_wwan" >> /etc/modprobe.d/blacklist.conf +echo -e "install qmi_wwan /bin/false" >> /etc/modprobe.d/blacklist.conf +echo -e "blacklisted qmi_wwan module" + +MODLOADED="`/sbin/lsmod | grep qmi_wwan`" +if [ "$MODLOADED" != "" ]; then + echo -e "qmi_wwan is found. Unloaded qmi_wwan module" + echo -e "cdc-wdm is found. Unloaded cdc-wdm module" + $QC_MODBIN_DIR/rmmod qmi_wwan.ko + $QC_MODBIN_DIR/rmmod cdc-wdm.ko + MODLOADED="`/sbin/lsmod | grep qmi_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "${RED}Failed to unload qmi_wwan.ko. Run manually sudo rmmod ModuleName${RESET}" + fi + MODLOADED="`/sbin/lsmod | grep cdc_wdm`" + if [ "$MODLOADED" != "" ]; then + echo -e "${RED}Failed to unload cdc_wdm.ko. Run manually sudo rmmod ModuleName${RESET}" + fi +fi +if [ -f $QC_QMI_WWAN/qmi_wwan.ko ]; then + echo -e "qmi_wwan is found. renamed to qmi_wwan_dup" + echo -e "cdc-wdm is found. renamed to cdc-wdm_dup" + mv /lib/modules/`uname -r`/kernel/drivers/usb/class/cdc-wdm.ko /lib/modules/`uname -r`/kernel/drivers/usb/class/cdc-wdm_dup + mv /lib/modules/`uname -r`/kernel/drivers/net/usb/qmi_wwan.ko /lib/modules/`uname -r`/kernel/drivers/net/usb/qmi_wwan_dup +fi + +MOD_EXIST="`grep -nr 'blacklist option' /etc/modprobe.d/blacklist.conf`" +if [ "$MOD_EXIST" != "" ]; then + sed -i '/option/d' $MODULE_BLACKLIST/blacklist.conf +fi +echo -e "blacklist option" >> /etc/modprobe.d/blacklist.conf +echo -e "install option /bin/false" >> /etc/modprobe.d/blacklist.conf +echo -e "blacklisted option module" + +MODLOADED="`/sbin/lsmod | grep option`" +if [ "$MODLOADED" != "" ]; then + echo -e "option is found. Unloaded option module" + $QC_MODBIN_DIR/rmmod option.ko + MODLOADED="`/sbin/lsmod | grep option`" + if [ "$MODLOADED" != "" ]; then + echo -e "${RED}Failed to unload option.ko. Run manually sudo rmmod option${RESET}" + fi +fi +if [ -f $QC_SERIAL/option.ko ]; then + echo -e "option is found. renamed to to option_dup" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/option.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial/option_dup +fi + +MOD_EXIST="`grep -nr 'blacklist usb_wwan' /etc/modprobe.d/blacklist.conf`" +if [ "$MOD_EXIST" != "" ]; then + sed -i '/usb_wwan/d' $MODULE_BLACKLIST/blacklist.conf +fi +echo -e "blacklist usb_wwan" >> /etc/modprobe.d/blacklist.conf +echo -e "install usb_wwan /bin/false" >> /etc/modprobe.d/blacklist.conf +echo -e "blacklisted usb_wwan module" + +MODLOADED="`/sbin/lsmod | grep usb_wwan`" +if [ "$MODLOADED" != "" ]; then + echo -e "usb_wwan is found. Unloaded usb_wwan module" + $QC_MODBIN_DIR/rmmod usb_wwan.ko + MODLOADED="`/sbin/lsmod | grep usb_wwan`" + if [ "$MODLOADED" != "" ]; then + echo -e "${RED}Failed to unload usb_wwan.ko. Run manually sudo rmmod ModuleName${RESET}" + fi +fi +if [ -f $QC_SERIAL/usb_wwan.ko ]; then + echo -e "usb_wwan is found. renamed to usb_wwan_dup" + mv /lib/modules/`uname -r`/kernel/drivers/usb/serial/usb_wwan.ko /lib/modules/`uname -r`/kernel/drivers/usb/serial/usb_wwan_dup +fi + +MODLOADED="`/sbin/lsmod | grep GobiSerial`" +if [ "$MODLOADED" != "" ]; then + ( $QC_MODBIN_DIR/rmmod $QC_MODULE_GOBISERIAL_NAME && echo -e "$QC_MODULE_GOBISERIAL_NAME removed successfully.." ) || { echo -e "$QC_MODULE_GOBISERIAL_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } +fi + +# echo -e "Loading module $QC_MODULE_GOBISERIAL_NAME" +# $QC_MODBIN_DIR/insmod $DEST_INS_SERIAL_PATH/$QC_MODULE_GOBISERIAL_NAME gQTIModemInfFilePath=$QC_MODEM_INF_PATH debug=0 + +MODLOADED="`/sbin/lsmod | grep QdssDiag`" +if [ "$MODLOADED" != "" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_QDSS_DIAG_NAME && echo -e "$QC_MODULE_QDSS_DIAG_NAME removed successfully..") || { echo -e "$QC_MODULE_QDSS_DIAG_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } +fi + +echo -e "Loading module dependency" +MODLOADED="`/sbin/lsmod | grep mii`" +if [ "$MODLOADED" == "" ]; then + echo -e "Loading module mii" + if [[ $OSName =~ "Red Hat Enterprise Linux" ]] || [[ $OSName =~ "Fedora Linux" ]] || [[ $OSName =~ "Ubuntu 24.04" ]]; then + if [ -f $QC_NET/mii.ko.xz ]; then + xz -d $QC_NET/mii.ko.xz + fi + if [ -f $QC_NET/mii.ko.zst ]; then + unzstd -d $QC_NET/mii.ko.zst + fi + if [ -f $QC_NET/mii.ko ]; then + $QC_MODBIN_DIR/insmod $QC_NET/mii.ko + fi + MODLOADED="`/sbin/lsmod | grep mii`" + if [ "$MODLOADED" == "" ]; then + echo -e "$OSName: mii.ko module not present at $QC_NET" + fi + else + $QC_MODBIN_DIR/insmod $QC_NET/mii.ko + fi +else + echo -e "Module mii already in place" +fi + +MODLOADED="`/sbin/lsmod | grep usbnet`" +if [ "$MODLOADED" == "" ]; then + echo -e "Loading module usbnet" + if [[ $OSName =~ "Red Hat Enterprise Linux" ]] || [[ $OSName =~ "Fedora Linux" ]] || [[ $OSName =~ "Ubuntu 24.04" ]]; then + if [ -f $QC_QMI_WWAN/usbnet.ko.xz ]; then + xz -d $QC_QMI_WWAN/usbnet.ko.xz + fi + if [ -f $QC_QMI_WWAN/usbnet.ko.zst ]; then + unzstd -d $QC_QMI_WWAN/usbnet.ko.zst + fi + if [ -f $QC_QMI_WWAN/usbnet.ko ]; then + $QC_MODBIN_DIR/insmod $QC_QMI_WWAN/usbnet.ko + fi + MODLOADED="`/sbin/lsmod | grep usbnet`" + if [ "$MODLOADED" == "" ]; then + echo -e "$OSName: usbnet.ko module not present at $QC_QMI_WWAN" + fi + else + $QC_MODBIN_DIR/insmod $QC_QMI_WWAN/usbnet.ko + fi +else + echo -e "Module usbnet already in place" +fi + +MODLOADED="`/sbin/lsmod | grep GobiNet`" +if [ "$MODLOADED" != "" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_RMNET_NAME && echo -e "$QC_MODULE_RMNET_NAME removed successfully..") || { echo -e "$QC_MODULE_RMNET_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } +fi + +MODLOADED="`/sbin/lsmod | grep qtiDevInf`" +if [ "$MODLOADED" != "" ]; then + ($QC_MODBIN_DIR/rmmod $QC_MODULE_INF_NAME && echo -e "$QC_MODULE_INF_NAME removed successfully..") || { echo -e "$QC_MODULE_INF_NAME in use"; echo -e "${RED}Note: ${CYAN} Close all applications that make use of the driver, including QUTS clients."; echo -e "${RED}ps -aux | grep QUTS, sudo kill -9 OR sudo pkill QUTS"; echo -e "${GREEN}Try $1ation again!"; exit 1; } +fi + +echo -e "Loading new module $QC_MODULE_INF_NAME" +$QC_MODBIN_DIR/insmod $DEST_INF_PATH/$QC_MODULE_INF_NAME debug_g=0 +MODLOADED="`/sbin/lsmod | grep qtiDevInf`" +if [ "$MODLOADED" == "" ]; then + echo -e "${RED}Failed to load new $QC_MODULE_INF_NAME module" + exit 1 +fi +echo -e "Loading new module $QC_MODULE_QDSS_DIAG_NAME" +$QC_MODBIN_DIR/insmod $DEST_QDSS_DAIG_PATH/$QC_MODULE_QDSS_DIAG_NAME gQdssInfFilePath=$QC_QDSS_INF_PATH gDiagInfFilePath=$QC_DIAG_INF_PATH debug_g=0 +MODLOADED="`/sbin/lsmod | grep QdssDiag`" +if [ "$MODLOADED" == "" ]; then + echo -e "${RED}Failed to load new $QC_MODULE_INF_NAME module" + exit 1 +fi +echo -e "Loading new module $QC_MODULE_RMNET_NAME" +$QC_MODBIN_DIR/insmod $DEST_INS_RMNET_PATH/$QC_MODULE_RMNET_NAME debug_g=0 debug_aggr=0 +MODLOADED="`/sbin/lsmod | grep GobiNet`" +if [ "$MODLOADED" == "" ]; then + echo -e "${RED}Failed to load new $QC_MODULE_INF_NAME module" + exit 1 +fi +# update modules.dep and modules.alias +depmod + +$QC_MAKE_DIR/find $DEST_QTI_PATH -type d -exec chmod 0755 {} \; + +echo -e "Qualcomm GobiNet driver is installed at $DEST_INS_RMNET_PATH" +echo -e "Qualcomm INF Parser driver is installed at $DEST_INF_PATH" +echo -e "Qualcomm QDSS/Diag driver is installed at $DEST_QDSS_DAIG_PATH" +echo -e "Qualcomm Modem driver is installed at $DEST_INS_SERIAL_PATH" +echo -e "Qualcomm Gobi device naming rules are installed at $QC_UDEV_PATH" + +if [ -f "$DEST_QUD_PATH/ReleaseNotes*.txt" ]; then + echo -e "QUD Release Notes available at $DEST_QUD_PATH" +fi + +MODUPDATE="`grep -r QCDevInf /etc/modules`" +if [ "$MODUPDATE" == "QCDevInf" ]; then + sed -i '/QCDevInf/d' /etc/modules +fi +MODUPDATE="`grep -nr qtiDevInf /etc/modules`" +if [ "$MODUPDATE" == "" ]; then + echo -e "qtiDevInf" >> /etc/modules +fi + +MODUPDATE="`grep -nr QdssDiag /etc/modules`" +if [ "$MODUPDATE" == "" ]; then + echo -e "QdssDiag" >> /etc/modules +fi + +MODUPDATE="`grep -nr GobiNet /etc/modules`" +if [ "$MODUPDATE" == "" ]; then + echo -e "GobiNet" >> /etc/modules +fi + +# MODUPDATE="`grep -nr GobiSerial /etc/modules`" +# if [ "$MODUPDATE" == "" ]; then +# echo -e "GobiSerial" >> /etc/modules +# fi + +if [[ $OSName != *"Red Hat Enterprise Linux"* ]]; then + MODUPDATE="`grep -nr 'iface usb0 inet static' /etc/network/interfaces`" + if [ "$MODUPDATE" == "" ]; then + echo -e "iface usb0 inet static" >> /etc/network/interfaces + fi +fi + +exit 0 diff --git a/src/linux/README.md b/src/linux/README.md index 1ffed05..7cb0bf2 100644 --- a/src/linux/README.md +++ b/src/linux/README.md @@ -1,3 +1,59 @@ +## Installing Qualcomm USB Kernel Drivers on Linux + +### Debian Package Installation (Recommended) + +The recommended way to install on Debian/Ubuntu systems is via the `.deb` package. It uses DKMS to automatically build, install, and manage the kernel modules. + +#### Build the Package + +```bash +# Install build dependencies (one-time) +sudo apt-get install debhelper dkms dpkg-dev dh-dkms linux-headers-$(uname -r) + +# Build from the repository root +./build-deb.sh +``` + +The version is read from `src/linux/version.h` and automatically synced to all packaging files. + +#### Install + +```bash +sudo dpkg -i ../qcom-usb-drivers-dkms__all.deb +``` + +#### Uninstall + +```bash +sudo dpkg -r qcom-usb-drivers-dkms +``` + +#### Upgrade + +Install the newer `.deb` directly — it automatically removes the old version, builds, and installs the new modules: + +```bash +sudo dpkg -i ../qcom-usb-drivers-dkms__all.deb +``` + +#### Check Status + +```bash +dpkg -s qcom-usb-drivers-dkms | grep Version +dkms status +lsmod | grep qti +``` + +#### What the Package Does + +- Builds 4 kernel modules via DKMS: `qtiDevInf`, `qcom_usb`, `qcom_usbnet`, `qcom-serial` +- Automatically rebuilds modules when the kernel is updated +- Blacklists conflicting in-tree modules (`qcserial`, `qmi_wwan`, `cdc_wdm`, `option`, `usb_wwan`) +- Installs udev rules for device permissions +- Cleans up pre-existing manual installs during installation +- Loads modules immediately after installation + +--- ## Building, Installing, and Uninstalling QUD Driver Modules (Without qcom_drivers.sh) @@ -35,7 +91,7 @@ Then execute: #### Note - Modifying qcom_drivers.sh is not recommended, as it is reserved for full installation and uninstallation workflows. -- For complete installations, dependency handling, conflict resolution, and thorough cleanup, we recommend using qcom_driver.sh +- For complete installations, dependency handling, conflict resolution, and thorough cleanup, we recommend using the deb package or qcom_driver.sh @@ -97,4 +153,4 @@ Step 3: If installation is successful, verify QUD status once again! ```bash lsmod | grep qti ``` -(you will see GobiNet, qtiDevInf, qdssdiag). If drivers are not present. Verify “/lib/modules/`uname -r`/kernel/drivers/net/usb” location for GobiNet, qtiDevInf, qdssdiag driver. \ No newline at end of file +(you will see GobiNet, qtiDevInf, qdssdiag). If drivers are not present. Verify "/lib/modules/`uname -r`/kernel/drivers/net/usb" location for GobiNet, qtiDevInf, qdssdiag driver. \ No newline at end of file diff --git a/src/linux/version.h b/src/linux/version.h index 95ed94c..9cbf24f 100644 --- a/src/linux/version.h +++ b/src/linux/version.h @@ -5,5 +5,5 @@ #ifndef VERSION_H #define VERSION_H -#define DRIVER_VERSION "1.0.6.0" +#define DRIVER_VERSION "1.0.6.1" #endif From 1dfc5e33007a842609bf0c27f56f482eeff3ecb4 Mon Sep 17 00:00:00 2001 From: hangz Date: Thu, 9 Apr 2026 00:03:40 -0700 Subject: [PATCH 2/3] fix: correct Conflicts/Replaces to use actual userspace package name The Conflicts and Replaces fields referenced non-existent package names (qcom-usb-userspace-drivers, qcom-usb-userspace-drivers-dkms). Fixed to use the actual installed package name: qcom-usb-userspace-driver. This enables apt to automatically resolve the mutual exclusion between kernel-mode and userspace driver packages during cross-installation. --- debian/control | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/control b/debian/control index f37d627..9c12d7a 100644 --- a/debian/control +++ b/debian/control @@ -9,8 +9,8 @@ Rules-Requires-Root: no Package: qcom-usb-drivers-dkms Architecture: all Depends: ${misc:Depends}, dkms, linux-headers-generic | linux-headers-amd64 | linux-headers-arm64 -Conflicts: qcom-usb-userspace-drivers, qcom-usb-userspace-drivers-dkms -Replaces: qcom-usb-userspace-drivers, qcom-usb-userspace-drivers-dkms +Conflicts: qcom-usb-userspace-driver +Replaces: qcom-usb-userspace-driver Description: Qualcomm USB kernel drivers (DKMS) DKMS package for Qualcomm USB kernel drivers including: * qtiDevInf - INF file parser for device enumeration From 8938e3a1d6acb9ddc468d759a1711025f44c6e0b Mon Sep 17 00:00:00 2001 From: Hang Zhao Date: Thu, 23 Apr 2026 21:28:35 -0700 Subject: [PATCH 3/3] QUD-1773: add smart Linux installer guide, preflight analyzer and known-good matrix Adds a self-analyzing preflight tool plus a human-readable install guide for qcom-usb-drivers-dkms on Linux, addressing five install pain points: 1. Too many distro variants 2. Kernel update breakage 3. Secure Boot MOK hurdles 4. Missing build components 5. Immutable / OSTree systems New files: - scripts/check-install-env.sh - Bash preflight analyzer. Detects distro, kernel, arch, Secure Boot state, kernel headers, DKMS/gcc/make toolchain, immutable rootfs, and conflicting in-tree modules. Emits human or JSON reports and exits 0/1/2/3/4 for pass/partial/fail/unknown/error. - scripts/known-good-configs.json - Curated compatibility matrix with a JSON Schema defined inline. Seeded with Ubuntu 22.04/24.04, Debian 11/12, Linux Mint, Pop!_OS, Fedora, RHEL, CentOS Stream and Arch. - scripts/match_known_good.py - Classifier that maps a detected (distro, version, kernel, arch) tuple to a known-good entry and prints status + notes + required packages for the bash driver to consume. - docs/linux/install-guide.md - Companion user guide: quick start, step-by-step install, finding-code troubleshooting matrix, distro notes, Secure Boot MOK enrollment, immutable rootfs workarounds, and a report-back workflow that invites users to submit PRs extending the known-good matrix. Integrations: - build-deb.sh now runs the preflight automatically; hard failures abort the build. Bypass with SKIP_PREFLIGHT=1. - README.md and docs/linux/getting-started/README.md link to the new install guide and advertise the preflight entry point. All three scripts pass bash -n / python ast parse / python -m json.tool validation on a clean Ubuntu 24.04 / 6.8 / x86_64 host, where the preflight correctly classifies the system as pass. --- README.md | 18 +- build-deb.sh | 17 ++ docs/linux/getting-started/README.md | 11 +- docs/linux/install-guide.md | 335 ++++++++++++++++++++++ scripts/check-install-env.sh | 406 +++++++++++++++++++++++++++ scripts/known-good-configs.json | 227 +++++++++++++++ scripts/match_known_good.py | 125 +++++++++ 7 files changed, 1135 insertions(+), 4 deletions(-) create mode 100644 docs/linux/install-guide.md create mode 100755 scripts/check-install-env.sh create mode 100644 scripts/known-good-configs.json create mode 100755 scripts/match_known_good.py diff --git a/README.md b/README.md index 6a65312..019c325 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ The project is organized to facilitate easy compilation, testing, and integratio ``` / ├─ debian/ # Debian packaging files for .deb package build -├─ docs/ # Architecture diagrams and design documents +├─ docs/ # Architecture diagrams, design documents and the Linux install guide +├─ scripts/ # Preflight installer tooling (check-install-env.sh, known-good-configs.json) ├─ src/ # Qualcomm USB kernel driver for windows and linux platform ├─ examples/ # Sample scripts ├─ build-deb.sh # Build script for generating the .deb package @@ -95,12 +96,25 @@ pnputil /add-driver /install The recommended way to install on Debian/Ubuntu systems is via the `.deb` package, which uses DKMS to automatically build kernel modules for the running kernel and handles install, uninstall, upgrade, and version control. +> **Before you start:** run the preflight environment analyzer to detect +> distro/kernel/Secure Boot/dependency issues and classify your system as +> `pass` / `partial` / `fail` / `unknown`: +> +> ```bash +> sudo bash scripts/check-install-env.sh +> ``` +> +> A full walkthrough with a troubleshooting matrix is available in +> [`docs/linux/install-guide.md`](./docs/linux/install-guide.md). The +> `build-deb.sh` script below runs the preflight automatically; set +> `SKIP_PREFLIGHT=1` to bypass it. + - Build the package ```bash # Install build dependencies (one-time) sudo apt-get install debhelper dkms dpkg-dev dh-dkms -# Build +# Build (runs preflight automatically) ./build-deb.sh ``` diff --git a/build-deb.sh b/build-deb.sh index 925700d..20d1f22 100755 --- a/build-deb.sh +++ b/build-deb.sh @@ -8,6 +8,23 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" +# Optional preflight environment check (QUD-1773). Set SKIP_PREFLIGHT=1 to +# bypass. Preflight failures (exit 2) abort the build; warnings are shown +# but allow the build to continue so CI and opt-in-unsupported developers +# are not blocked. +if [ -z "${SKIP_PREFLIGHT:-}" ] && [ -x "$SCRIPT_DIR/scripts/check-install-env.sh" ]; then + echo "Running install environment preflight (set SKIP_PREFLIGHT=1 to bypass)..." + set +e + bash "$SCRIPT_DIR/scripts/check-install-env.sh" --no-color + preflight_rc=$? + set -e + if [ "$preflight_rc" -ge 2 ]; then + echo "Preflight reported a hard failure (exit $preflight_rc). Fix the" + echo "items above, or re-run with SKIP_PREFLIGHT=1 to force a build." + exit "$preflight_rc" + fi +fi + PKG_VERSION=$(grep '#define DRIVER_VERSION' src/linux/version.h | awk '{print $3}' | tr -d '"') echo "Building qcom-usb-drivers-dkms version $PKG_VERSION" diff --git a/docs/linux/getting-started/README.md b/docs/linux/getting-started/README.md index f3d4441..cf0828a 100644 --- a/docs/linux/getting-started/README.md +++ b/docs/linux/getting-started/README.md @@ -1,2 +1,9 @@ -For more guidance on build process, FAQ's and troubleshooting, please refer to -[README](../../../src/linux/README.md) document. +# Getting started with Qualcomm USB Kernel Drivers on Linux + +- Install / build walkthrough with preflight and a troubleshooting matrix: + see [`docs/linux/install-guide.md`](../install-guide.md). +- Developer notes, Makefile usage and FAQs: + see [`src/linux/README.md`](../../../src/linux/README.md). +- Run the preflight analyzer before installing: + + sudo bash scripts/check-install-env.sh \ No newline at end of file diff --git a/docs/linux/install-guide.md b/docs/linux/install-guide.md new file mode 100644 index 0000000..93cca6b --- /dev/null +++ b/docs/linux/install-guide.md @@ -0,0 +1,335 @@ +# Qualcomm USB Kernel Drivers — Linux Installer Guide + +This guide walks you through installing the Qualcomm USB kernel drivers +(`qcom-usb-drivers-dkms`) on Linux, and diagnoses the most common issues that +can arise across distributions, kernels, Secure Boot states, and immutable +systems. + +It is the **human-readable companion** to the preflight tool: + +```bash +sudo bash scripts/check-install-env.sh +``` + +The preflight tool detects your environment, cross-references it against +[`scripts/known-good-configs.json`](../../scripts/known-good-configs.json), +and classifies the system as `pass` / `partial` / `fail` / `unknown`. Every +finding in its report maps to a section in this guide. + +--- + +## Table of contents + +1. [Quick start](#quick-start) +2. [Run the preflight check first](#run-the-preflight-check-first) +3. [Supported environments at a glance](#supported-environments-at-a-glance) +4. [Step-by-step install](#step-by-step-install) +5. [Troubleshooting by finding code](#troubleshooting-by-finding-code) +6. [Distribution-specific notes](#distribution-specific-notes) +7. [Secure Boot](#secure-boot) +8. [Immutable / OSTree systems](#immutable--ostree-systems) +9. [Reporting results back](#reporting-results-back) + +--- + +## Quick start + +On Ubuntu 22.04 / 24.04 or Debian 12: + +```bash +git clone https://github.com/qualcomm/qcom-usb-kernel-drivers.git +cd qcom-usb-kernel-drivers +sudo apt-get install -y dkms debhelper dpkg-dev dh-dkms "linux-headers-$(uname -r)" +sudo bash scripts/check-install-env.sh # preflight +./build-deb.sh +sudo dpkg -i ../qcom-usb-drivers-dkms_*_all.deb +``` + +On other distributions, use the `Makefile` under `src/linux/` directly — +see [Distribution-specific notes](#distribution-specific-notes). + +--- + +## Run the preflight check first + +```bash +sudo bash scripts/check-install-env.sh [--json] [--report-file report.json] [--strict] +``` + +Exit codes: + +| Code | Status | Meaning | +|------|----------|---------------------------------------------------------------------| +| 0 | pass | System matches a known-good configuration and all deps are present. | +| 1 | partial | Supported but with caveats (see the warnings printed). | +| 2 | fail | Hard prerequisite missing or known-bad combination. | +| 3 | unknown | System isn't in `known-good-configs.json` yet. | +| 4 | error | The script itself failed. | + +Copy the JSON report into any bug report or JIRA ticket so maintainers see +your exact configuration in one step: + +```bash +sudo bash scripts/check-install-env.sh --json --report-file install-report.json +``` + +--- + +## Supported environments at a glance + +The authoritative source of truth is +[`scripts/known-good-configs.json`](../../scripts/known-good-configs.json). +A snapshot at the time of writing: + +| Distribution | Versions | Arches | Status | Notes | +|---------------|---------------------|------------------|-----------|---------------------------------------------------------| +| Ubuntu | 22.04, 24.04 LTS | x86_64, arm64 | pass | Primary development target. | +| Debian | 12 | x86_64, arm64 | pass | Use `linux-headers-arm64` on ARM. | +| Debian | 11 | x86_64 | partial | `dh-dkms` not available; build-deb.sh falls back. | +| Linux Mint | 21.x | x86_64 | pass | Behaves as Ubuntu 22.04. | +| Pop!_OS | 22.04 | x86_64 | pass | Signed kernel; MOK enrollment straightforward. | +| Fedora | 40, 41 | x86_64, aarch64 | untested | Use the `src/linux` Makefile; `.deb` does not apply. | +| Fedora Silverblue / Kinoite | any | x86_64, aarch64 | untested | Immutable rootfs; requires `rpm-ostree` layering. | +| RHEL | 9 | x86_64, aarch64 | untested | `dkms` via EPEL; use Makefile. | +| CentOS Stream | 9 | x86_64, aarch64 | partial | RMNET crash under investigation (QUD-1629). | +| Arch Linux | rolling | x86_64 | untested | Rolling release; Makefile path only. | + +> **If your system is not in the table**, the preflight tool will mark it +> `unknown`. That is not a failure — please +> [report results back](#reporting-results-back) so we can add your +> combination. + +--- + +## Step-by-step install + +### 1. Install build dependencies + +| Distro family | Command | +|----------------|------------------------------------------------------------------------------------------------| +| Debian / Ubuntu| `sudo apt-get install -y dkms debhelper dpkg-dev dh-dkms "linux-headers-$(uname -r)"` | +| Fedora | `sudo dnf install -y dkms kernel-devel kernel-headers make gcc` | +| RHEL / CentOS | `sudo dnf install -y epel-release && sudo dnf install -y dkms kernel-devel kernel-headers make gcc` | +| openSUSE | `sudo zypper install -y dkms kernel-devel make gcc` | +| Arch | `sudo pacman -S --needed dkms linux-headers base-devel` | + +### 2. Run the preflight check + +```bash +sudo bash scripts/check-install-env.sh +``` + +Address any `[FAIL]` items before proceeding. `[WARN]` items are informational +but will be surfaced again during the install itself (e.g. Secure Boot MOK +enrollment, conflicting in-tree modules). + +### 3. Build and install + +**Debian / Ubuntu (recommended):** + +```bash +./build-deb.sh +sudo dpkg -i ../qcom-usb-drivers-dkms_*_all.deb +``` + +The `.deb` postinstall script will: + +- Register the driver with DKMS and build modules for the running kernel. +- Blacklist conflicting in-tree modules (`qcserial`, `qmi_wwan`, `cdc_wdm`, + `option`, `usb_wwan`) via `/etc/modprobe.d/qcom-usb-drivers-dkms.conf`. +- Install udev rules and reload `udevadm`. +- `modprobe` all four Qualcomm modules in the correct order. + +**Other distributions:** + +```bash +cd src/linux +make +sudo make install +``` + +### 4. Verify + +```bash +dpkg -s qcom-usb-drivers-dkms | grep Version # Debian/Ubuntu only +dkms status +lsmod | grep -E 'qtiDevInf|qcom_usb|qcom_usbnet|qcom-serial' +``` + +Plug in a Qualcomm USB device — you should see `/dev/qcqmi*` and +`/dev/ttyUSB*` entries appear. + +--- + +## Troubleshooting by finding code + +Every finding emitted by `check-install-env.sh` has a stable code. Find the +code in the table below for remediation details. + +| Code | Level | What it means | Fix | +|---------------------------|-------|--------------------------------------------------------------------|------------------------------------------------------------------------------------------------------| +| `NO_OS_RELEASE` | WARN | `/etc/os-release` is missing. | Run on a standard Linux distro; container images sometimes strip it. | +| `IMMUTABLE_ROOTFS` | WARN | OSTree/Silverblue/Kinoite/MicroOS detected. | See [Immutable / OSTree systems](#immutable--ostree-systems). | +| `SECURE_BOOT_MOK` | WARN | Secure Boot is on but no DKMS/MOK key is enrolled. | See [Secure Boot](#secure-boot). | +| `MISSING_HEADERS` | FAIL | Kernel headers for the running kernel are not installed. | `sudo apt-get install -y "linux-headers-$(uname -r)"` (or distro equivalent). | +| `MISSING_DKMS` | FAIL | `dkms` is not installed. | `sudo apt-get install -y dkms` (Debian/Ubuntu), `sudo dnf install -y dkms` (Fedora/EPEL). | +| `MISSING_GCC` | FAIL | No C compiler found. | `sudo apt-get install -y build-essential` / `sudo dnf groupinstall -y 'Development Tools'`. | +| `MISSING_MAKE` | FAIL | `make` not installed. | Included in the build-essential / Development Tools / base-devel meta-packages above. | +| `MISSING_DEB_BUILD_DEPS` | WARN | `debhelper`, `dpkg-dev`, or `dh-dkms` missing on a Debian host. | `sudo apt-get install -y debhelper dpkg-dev dh-dkms`. | +| `CONFLICTING_MODULES` | WARN | In-tree modules that collide with the Qualcomm drivers are loaded. | The `.deb` postinstall handles this; if installing manually: `sudo modprobe -r `. | +| `NO_CONFIGS_FILE` | WARN | `known-good-configs.json` is missing from the repo. | Re-clone the repo — this file ships with the source tree. | +| `NO_PYTHON` | WARN | `python3` is not available for the classifier. | `sudo apt-get install -y python3` (or distro equivalent). | +| `NO_MATCHER` | WARN | `scripts/match_known_good.py` is missing. | Re-clone the repo. | +| `MATCH_PASS` | INFO | You are on a known-good combination. | No action needed. | +| `MATCH_PARTIAL` | WARN | Supported but with caveats (see printed notes). | Follow the caveat's mitigation. | +| `MATCH_FAIL` | FAIL | This exact combo is known to fail. | Upgrade kernel/distro, or pick a supported entry. | +| `MATCH_UNTESTED` | WARN | Configuration listed but not yet validated. | Proceed and [report results](#reporting-results-back). | +| `MATCH_UNKNOWN` | WARN | No matching entry in `known-good-configs.json`. | Proceed if `dkms`/headers are present, then [report results](#reporting-results-back). | + +--- + +## Distribution-specific notes + +### Ubuntu / Linux Mint / Pop!_OS + +- Use the `.deb` flow exactly as in [Quick start](#quick-start). +- On HWE kernels, install `linux-headers-generic-hwe-22.04` (or equivalent). +- On Pop!_OS, the kernel is System76-signed; MOK enrollment is straightforward. + +### Debian 12 + +- Identical flow to Ubuntu, but headers are named `linux-headers-amd64` or + `linux-headers-arm64`. + +### Debian 11 + +- `dh-dkms` is not packaged. `build-deb.sh` detects this and falls back to + `dh_dkms`. Newer packaging features (e.g., automatic signing integration) + are unavailable; the drivers themselves work. + +### Fedora / RHEL / CentOS Stream + +- `.deb` packaging does not apply. Use the raw build path: + + ```bash + sudo dnf install -y dkms kernel-devel kernel-headers make gcc + cd src/linux + make + sudo make install + ``` + +- On RHEL, enable EPEL first: `sudo dnf install -y epel-release`. +- An RPM package is a planned future contribution; track it via the + `scripts/known-good-configs.json` entries marked `untested`. + +### openSUSE Leap / Tumbleweed + +- Use zypper to install build deps, then the Makefile path as for Fedora. + +### Arch Linux + +- Rolling release; kernel headers change often. After every kernel upgrade, + run `sudo dkms autoinstall` (automatic if you keep `dkms.service` enabled). + +--- + +## Secure Boot + +If the preflight emits `SECURE_BOOT_MOK`: + +1. On first install, DKMS prints instructions and generates a Machine Owner + Key under `/var/lib/dkms/mok.{pub,key}`. +2. Enroll it: + ```bash + sudo mokutil --import /var/lib/dkms/mok.pub + ``` + You'll be prompted for a one-time password. +3. Reboot. On the next boot, the UEFI **MOK Manager** screen will appear. + Choose *Enroll MOK* → *Continue* → *Yes* and enter the password. +4. After boot, verify: + ```bash + mokutil --list-enrolled | grep -i DKMS + lsmod | grep qtiDevInf + ``` + +If Secure Boot is enabled and no MOK is enrolled, DKMS-built modules will +**silently fail to load**. Re-running `check-install-env.sh` after reboot +should now show `MOK enrolled: yes`. + +--- + +## Immutable / OSTree systems + +Fedora Silverblue, Kinoite, IoT Core, openSUSE MicroOS and similar +transactional systems keep `/usr` read-only, so the standard `dkms install` +flow cannot write into `/lib/modules`. + +Two supported workarounds: + +### 1. Layer the package with rpm-ostree (Silverblue / Kinoite) + +```bash +sudo rpm-ostree install dkms kernel-devel akmods +# Build and install the Qualcomm drivers from src/linux into the new deployment: +cd src/linux +sudo rpm-ostree usroverlay # opens a writable transient overlay +make && sudo make install +sudo systemctl reboot +``` + +Because `usroverlay` is transient, you'll need to either: + +- Package the built modules into a small RPM and layer that with + `rpm-ostree install`, **or** +- Rebuild after every OSTree update. + +### 2. Use a toolbox / distrobox container + +```bash +toolbox create --image registry.fedoraproject.org/fedora:40 +toolbox enter +sudo dnf install -y dkms kernel-devel-$(uname -r) kernel-headers make gcc +# Then clone and build inside the container; copy .ko files out. +``` + +This path is only useful for *development* — the host still won't load +unsigned modules under Secure Boot without MOK enrollment. + +--- + +## Reporting results back + +The whole point of the preflight + known-good-configs architecture is that +every user can extend our coverage with a single PR. + +1. Run the preflight and save the JSON: + + ```bash + sudo bash scripts/check-install-env.sh --json --report-file install-report.json + ``` + +2. Attempt the install. + +3. Edit [`scripts/known-good-configs.json`](../../scripts/known-good-configs.json) + and append an entry like: + + ```json + { + "distro_id": "ubuntu", + "distro_version": "25.04", + "kernel_range": "6.14..6.14", + "arch": ["x86_64"], + "status": "pass", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-generic"], + "notes": "Clean install succeeded; 4 modules load; USB device enumerates.", + "validated_by": "your-github-handle", + "validated_on": "2026-05-01" + } + ``` + +4. Open a PR titled `chore(installer): add known-good entry for ` + and attach the `install-report.json` to the PR description. + +Over time the file becomes a living compatibility matrix, and the preflight +tool's classifier gets more accurate for every user that contributes. diff --git a/scripts/check-install-env.sh b/scripts/check-install-env.sh new file mode 100755 index 0000000..2f903f3 --- /dev/null +++ b/scripts/check-install-env.sh @@ -0,0 +1,406 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause-Clear +# +# check-install-env.sh - Preflight environment analyzer for qcom-usb-drivers-dkms. +# +# Detects distro/kernel/arch/Secure Boot/DKMS state, cross-references +# scripts/known-good-configs.json (via scripts/match_known_good.py) and +# classifies the system as pass | partial | fail | unknown with concrete +# remediation hints. +# +# Usage: sudo bash scripts/check-install-env.sh [options] +# --json Emit JSON report on stdout +# --report-file PATH Also write the JSON report to PATH +# --configs-file PATH Use an alternate known-good-configs.json +# --no-color Disable ANSI color +# --strict Exit non-zero unless status == pass +# -h, --help Show this help +# +# Exit: 0=pass 1=partial 2=fail 3=unknown 4=script error + +set -u + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIGS_FILE="${SCRIPT_DIR}/known-good-configs.json" +MATCHER="${SCRIPT_DIR}/match_known_good.py" +REPORT_FILE="" +MODE="text" +COLOR=1 +STRICT=0 + +declare -a FINDINGS=() +STATUS="pass" +MATCH_STATUS="unknown" +MATCH_NOTES="" +MATCH_REQ="" + +D_ID="" D_VER="" D_PRETTY="" +KERNEL="" KMM="" ARCH="" +SB="unknown" MOK="unknown" +HDR_OK=0 HDR_PATH="" +DKMS_VER="" GCC_VER="" MAKE_VER="" +IMMUTABLE=0 PM="unknown" +CONFLICTS="" + +cc() { + local code=$1; shift + if [[ $COLOR -eq 1 && -t 1 ]]; then printf '\033[%sm%s\033[0m' "$code" "$*" + else printf '%s' "$*"; fi +} +red() { cc '1;31' "$@"; } +green() { cc '1;32' "$@"; } +yellow() { cc '1;33' "$@"; } +blue() { cc '1;34' "$@"; } +bold() { cc '1' "$@"; } + +add() { + FINDINGS+=("$1|$2|$3|${4-}") + case "$1" in + error) [[ "$STATUS" != "fail" ]] && STATUS="fail" ;; + warn) [[ "$STATUS" == "pass" ]] && STATUS="partial" ;; + esac +} + +have() { command -v "$1" >/dev/null 2>&1; } + +je() { + local s=${1-} + s=${s//\\/\\\\}; s=${s//\"/\\\"} + s=${s//$'\n'/\\n}; s=${s//$'\r'/\\r}; s=${s//$'\t'/\\t} + printf '%s' "$s" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --json) MODE="json"; shift ;; + --report-file) REPORT_FILE="$2"; shift 2 ;; + --configs-file) CONFIGS_FILE="$2"; shift 2 ;; + --no-color) COLOR=0; shift ;; + --strict) STRICT=1; shift ;; + -h|--help) sed -n '2,20p' "$0" | sed 's/^# \?//'; exit 0 ;; + *) echo "Unknown option: $1" >&2; exit 4 ;; + esac +done + +detect_distro() { + if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + D_ID="${ID:-unknown}" + D_VER="${VERSION_ID:-unknown}" + D_PRETTY="${PRETTY_NAME:-$D_ID}" + else + D_ID="unknown"; D_VER="unknown"; D_PRETTY="unknown" + add warn NO_OS_RELEASE "/etc/os-release not found" "Run on a standard Linux distro." + fi + if have rpm-ostree && rpm-ostree status >/dev/null 2>&1; then + IMMUTABLE=1 + add warn IMMUTABLE_ROOTFS "Immutable (OSTree) filesystem; /usr is read-only." \ + "Use 'rpm-ostree install dkms kernel-devel' or a toolbox container." + fi + if have apt-get; then PM="apt" + elif have dnf; then PM="dnf" + elif have yum; then PM="yum" + elif have zypper; then PM="zypper" + elif have pacman; then PM="pacman" + fi +} + +detect_kernel_arch() { + KERNEL="$(uname -r)" + ARCH="$(uname -m)" + KMM="$(printf '%s' "$KERNEL" | awk -F. '{printf "%s.%s", $1, $2}')" +} + +detect_secure_boot() { + if have mokutil; then + local out; out="$(mokutil --sb-state 2>/dev/null || true)" + case "$out" in + *"SecureBoot enabled"*) SB="enabled" ;; + *"SecureBoot disabled"*) SB="disabled" ;; + esac + elif [[ -d /sys/firmware/efi ]]; then + local v; v="$(find /sys/firmware/efi/efivars -maxdepth 1 -name 'SecureBoot-*' 2>/dev/null | head -n1)" + if [[ -n "$v" ]]; then + if od -An -tu1 -j4 -N1 "$v" 2>/dev/null | grep -q '1'; then SB="enabled" + else SB="disabled"; fi + fi + fi + if [[ "$SB" == "enabled" ]]; then + if have mokutil && mokutil --list-enrolled 2>/dev/null | grep -qi 'DKMS\|MOK'; then + MOK="yes" + else + MOK="no" + add warn SECURE_BOOT_MOK "Secure Boot enabled but no DKMS/MOK key enrolled." \ + "DKMS will prompt to create and enroll a MOK on install (mokutil --import)." + fi + fi +} + +detect_headers() { + local p + for p in "/lib/modules/$KERNEL/build" "/usr/lib/modules/$KERNEL/build" \ + "/usr/src/linux-headers-$KERNEL" "/usr/src/kernels/$KERNEL"; do + if [[ -d "$p" && -e "$p/Makefile" ]]; then + HDR_OK=1; HDR_PATH="$p"; break + fi + done + if [[ $HDR_OK -eq 0 ]]; then + local h + case "$PM" in + apt) h="sudo apt-get install -y linux-headers-$KERNEL || sudo apt-get install -y linux-headers-generic" ;; + dnf) h="sudo dnf install -y kernel-devel-$KERNEL kernel-headers" ;; + yum) h="sudo yum install -y kernel-devel-$KERNEL kernel-headers" ;; + zypper) h="sudo zypper install -y kernel-devel=$KERNEL" ;; + pacman) h="sudo pacman -S --needed linux-headers" ;; + *) h="Install kernel headers for $KERNEL via your package manager." ;; + esac + add error MISSING_HEADERS "Kernel headers for $KERNEL not found; DKMS cannot build." "$h" + fi +} + +detect_toolchain() { + if have dkms; then + DKMS_VER="$(dkms --version 2>/dev/null | head -n1 | awk '{print $NF}')" + else + local h + case "$PM" in + apt) h="sudo apt-get install -y dkms" ;; + dnf|yum) h="sudo dnf install -y dkms # enable EPEL on RHEL" ;; + zypper) h="sudo zypper install -y dkms" ;; + pacman) h="sudo pacman -S --needed dkms" ;; + *) h="Install dkms via your package manager." ;; + esac + add error MISSING_DKMS "dkms not installed." "$h" + fi + if have gcc; then GCC_VER="$(gcc -dumpversion 2>/dev/null)" + else add error MISSING_GCC "gcc not installed." "Install build-essential / Development Tools / base-devel." + fi + if have make; then MAKE_VER="$(make --version 2>/dev/null | head -n1 | awk '{print $NF}')" + else add error MISSING_MAKE "make not installed." "Install make via your package manager." + fi + if [[ "$PM" == "apt" ]]; then + local pkg missing=() + for pkg in debhelper dpkg-dev; do + dpkg -s "$pkg" >/dev/null 2>&1 || missing+=("$pkg") + done + if ! dpkg -s dh-dkms >/dev/null 2>&1; then missing+=("dh-dkms"); fi + if [[ ${#missing[@]} -gt 0 ]]; then + add warn MISSING_DEB_BUILD_DEPS "Missing Debian build deps: ${missing[*]}" \ + "sudo apt-get install -y ${missing[*]}" + fi + fi +} + +detect_conflicts() { + local m loaded=() + for m in qcserial qmi_wwan cdc_wdm option usb_wwan; do + if lsmod 2>/dev/null | awk '{print $1}' | grep -qx "$m"; then + loaded+=("$m") + fi + done + if [[ ${#loaded[@]} -gt 0 ]]; then + CONFLICTS="${loaded[*]}" + add warn CONFLICTING_MODULES "In-tree modules loaded that conflict with Qualcomm drivers: ${loaded[*]}" \ + "The .deb postinst blacklists them. Manual: sudo modprobe -r ${loaded[*]}" + fi +} + +match_known_good() { + if [[ ! -r "$CONFIGS_FILE" ]]; then + MATCH_STATUS="unknown" + MATCH_NOTES="known-good-configs.json not found" + add warn NO_CONFIGS_FILE "$MATCH_NOTES at $CONFIGS_FILE" "Skipping known-good matching." + return + fi + local py="" + if have python3; then py="python3" + elif have python; then py="python" + else + MATCH_STATUS="unknown" + MATCH_NOTES="python3 not available" + add warn NO_PYTHON "$MATCH_NOTES" "Install python3 to enable the classifier." + return + fi + if [[ ! -r "$MATCHER" ]]; then + MATCH_STATUS="unknown" + MATCH_NOTES="matcher script not found" + add warn NO_MATCHER "$MATCHER missing" "Re-clone the repository to restore scripts/match_known_good.py." + return + fi + local line status notes req + line="$("$py" "$MATCHER" "$CONFIGS_FILE" "$D_ID" "$D_VER" "$KMM" "$ARCH" 2>/dev/null || true)" + IFS=$'\t' read -r status notes req <<<"${line:-unknown no output }" + MATCH_STATUS="${status:-unknown}" + MATCH_NOTES="${notes:-}" + MATCH_REQ="${req:-}" + case "$MATCH_STATUS" in + pass) + add info MATCH_PASS "System matches a known-good configuration." "" + ;; + partial) + add warn MATCH_PARTIAL "System matches a partially-supported configuration: ${MATCH_NOTES}" \ + "Proceed with caution; see the note above for caveats." + ;; + fail) + add error MATCH_FAIL "System matches a known-bad configuration: ${MATCH_NOTES}" \ + "Upgrade the kernel/distro, or try a supported entry in known-good-configs.json." + ;; + untested) + add warn MATCH_UNTESTED "Configuration is recognized but untested: ${MATCH_NOTES}" \ + "Proceed and report results (pass/fail) back via a PR to scripts/known-good-configs.json." + ;; + unknown|*) + add warn MATCH_UNKNOWN "No matching entry in known-good-configs.json for ${D_ID} ${D_VER} / kernel ${KMM} / ${ARCH}." \ + "After a successful install, submit a PR appending this combination." + [[ "$STATUS" == "pass" ]] && STATUS="unknown" + ;; + esac +} + +# --- rendering ------------------------------------------------------------- + +render_text() { + echo + bold "Qualcomm USB Kernel Drivers - Install Environment Report"; echo + echo "========================================================" + echo + printf " Distribution : %s (%s %s)\n" "$D_PRETTY" "$D_ID" "$D_VER" + printf " Kernel : %s (%s)\n" "$KERNEL" "$KMM" + printf " Architecture : %s\n" "$ARCH" + printf " Package manager : %s\n" "$PM" + printf " Immutable rootfs : %s\n" "$([[ $IMMUTABLE -eq 1 ]] && echo yes || echo no)" + printf " Secure Boot : %s" "$SB" + [[ "$SB" == "enabled" ]] && printf " (MOK enrolled: %s)" "$MOK" + echo + printf " Kernel headers : " + if [[ $HDR_OK -eq 1 ]]; then green "found"; printf " (%s)\n" "$HDR_PATH" + else red "missing"; echo; fi + printf " dkms : %s\n" "${DKMS_VER:-not installed}" + printf " gcc : %s\n" "${GCC_VER:-not installed}" + printf " make : %s\n" "${MAKE_VER:-not installed}" + printf " Conflicting mods : %s\n" "${CONFLICTS:-none}" + echo + printf " Known-good match : " + case "$MATCH_STATUS" in + pass) green "$MATCH_STATUS" ;; + partial) yellow "$MATCH_STATUS" ;; + fail) red "$MATCH_STATUS" ;; + untested) yellow "$MATCH_STATUS" ;; + *) yellow "$MATCH_STATUS" ;; + esac + echo + [[ -n "$MATCH_NOTES" ]] && printf " %s\n" "$MATCH_NOTES" + echo + echo "Findings" + echo "--------" + if [[ ${#FINDINGS[@]} -eq 0 ]]; then + green " All checks passed."; echo + else + local f level code msg hint + for f in "${FINDINGS[@]}"; do + IFS='|' read -r level code msg hint <<<"$f" + case "$level" in + error) red " [FAIL] " ;; + warn) yellow " [WARN] " ;; + info) green " [INFO] " ;; + *) printf " [----] " ;; + esac + printf "%s: %s\n" "$code" "$msg" + [[ -n "$hint" ]] && printf " %s %s\n" "$(blue '>')" "$hint" + done + fi + echo + printf "Overall status : " + case "$STATUS" in + pass) green "PASS" ;; + partial) yellow "PARTIAL" ;; + fail) red "FAIL" ;; + unknown) yellow "UNKNOWN" ;; + esac + echo; echo +} + +render_json() { + local out + out+='{' + out+='"tool":"check-install-env",' + out+='"schema_version":"1.0.0",' + out+='"timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",' + out+='"status":"'"$(je "$STATUS")"'",' + out+='"system":{' + out+='"distro_id":"'"$(je "$D_ID")"'",' + out+='"distro_version":"'"$(je "$D_VER")"'",' + out+='"distro_pretty":"'"$(je "$D_PRETTY")"'",' + out+='"kernel":"'"$(je "$KERNEL")"'",' + out+='"kernel_major_minor":"'"$(je "$KMM")"'",' + out+='"arch":"'"$(je "$ARCH")"'",' + out+='"package_manager":"'"$(je "$PM")"'",' + out+='"immutable_rootfs":'"$([[ $IMMUTABLE -eq 1 ]] && echo true || echo false)"',' + out+='"secure_boot":"'"$(je "$SB")"'",' + out+='"mok_enrolled":"'"$(je "$MOK")"'",' + out+='"kernel_headers_present":'"$([[ $HDR_OK -eq 1 ]] && echo true || echo false)"',' + out+='"kernel_headers_path":"'"$(je "$HDR_PATH")"'",' + out+='"dkms_version":"'"$(je "$DKMS_VER")"'",' + out+='"gcc_version":"'"$(je "$GCC_VER")"'",' + out+='"make_version":"'"$(je "$MAKE_VER")"'",' + out+='"conflicting_modules":"'"$(je "$CONFLICTS")"'"' + out+='},' + out+='"known_good_match":{' + out+='"status":"'"$(je "$MATCH_STATUS")"'",' + out+='"notes":"'"$(je "$MATCH_NOTES")"'",' + out+='"required_packages":"'"$(je "$MATCH_REQ")"'"' + out+='},' + out+='"findings":[' + local first=1 f level code msg hint + for f in "${FINDINGS[@]}"; do + IFS='|' read -r level code msg hint <<<"$f" + [[ $first -eq 0 ]] && out+=',' + first=0 + out+='{' + out+='"level":"'"$(je "$level")"'",' + out+='"code":"'"$(je "$code")"'",' + out+='"message":"'"$(je "$msg")"'",' + out+='"hint":"'"$(je "$hint")"'"' + out+='}' + done + out+=']}' + printf '%s\n' "$out" +} + +# --- main ------------------------------------------------------------------ + +detect_distro +detect_kernel_arch +detect_secure_boot +detect_headers +detect_toolchain +detect_conflicts +match_known_good + +if [[ "$MODE" == "json" ]]; then + json_out="$(render_json)" + printf '%s\n' "$json_out" + if [[ -n "$REPORT_FILE" ]]; then + printf '%s\n' "$json_out" > "$REPORT_FILE" + fi +else + render_text + if [[ -n "$REPORT_FILE" ]]; then + render_json > "$REPORT_FILE" + echo "JSON report written to: $REPORT_FILE" + fi +fi + +case "$STATUS" in + pass) code=0 ;; + partial) code=1 ;; + fail) code=2 ;; + unknown) code=3 ;; + *) code=4 ;; +esac + +if [[ "$STRICT" -eq 1 && "$STATUS" != "pass" ]]; then + exit "$code" +fi +exit "$code" diff --git a/scripts/known-good-configs.json b/scripts/known-good-configs.json new file mode 100644 index 0000000..62fdc5e --- /dev/null +++ b/scripts/known-good-configs.json @@ -0,0 +1,227 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/qualcomm/qcom-usb-kernel-drivers/scripts/known-good-configs.json", + "title": "Qualcomm USB Kernel Drivers - Known-Good Install Configurations", + "description": "Curated list of distro/kernel/architecture combinations that have been validated for the qcom-usb-drivers-dkms package. The preflight script (check-install-env.sh) consults this file to classify the current system as 'known-good', 'known-bad', or 'untested'. New validation results can be appended by contributors via pull request.", + "type": "object", + "properties": { + "schema_version": { + "type": "string", + "description": "Semantic version of this file's schema. Bump when the structure changes." + }, + "last_updated": { + "type": "string", + "format": "date", + "description": "ISO-8601 date of the last edit to this file." + }, + "driver_versions_covered": { + "type": "array", + "description": "List of driver package versions this data applies to.", + "items": { "type": "string" } + }, + "entries": { + "type": "array", + "description": "One entry per validated (or invalidated) system configuration.", + "items": { "$ref": "#/$defs/entry" } + } + }, + "required": ["schema_version", "entries"], + "$defs": { + "entry": { + "type": "object", + "required": ["distro_id", "distro_version", "kernel_range", "arch", "status"], + "properties": { + "distro_id": { + "type": "string", + "description": "Value of ID from /etc/os-release (e.g., 'ubuntu', 'debian', 'fedora', 'rhel', 'centos', 'linuxmint', 'pop').", + "examples": ["ubuntu", "debian", "fedora"] + }, + "distro_version": { + "type": "string", + "description": "Value of VERSION_ID from /etc/os-release, or a glob pattern (e.g., '22.04', '24.*', '11').", + "examples": ["22.04", "24.04", "11", "12", "40"] + }, + "kernel_range": { + "type": "string", + "description": "Kernel version range this entry applies to, expressed as 'min..max' inclusive. Use '*' for unbounded.", + "examples": ["5.15..6.8", "6.0..*", "*..6.6"] + }, + "arch": { + "type": "array", + "description": "Architectures this entry is valid for.", + "items": { + "type": "string", + "enum": ["x86_64", "amd64", "aarch64", "arm64", "i686"] + } + }, + "status": { + "type": "string", + "description": "Outcome of the last validation run on this combination.", + "enum": ["pass", "fail", "partial", "untested"] + }, + "secure_boot": { + "type": "string", + "description": "Secure Boot compatibility note.", + "enum": ["supported", "manual-mok-required", "unsupported", "not-applicable"], + "default": "not-applicable" + }, + "immutable_rootfs": { + "type": "boolean", + "description": "True for OSTree/Silverblue/immutable distros. These typically require rpm-ostree layering or a container-based build.", + "default": false + }, + "required_packages": { + "type": "array", + "description": "Distro-specific package names needed before attempting install.", + "items": { "type": "string" } + }, + "notes": { + "type": "string", + "description": "Free-form human-readable notes: known caveats, workarounds, links to issues." + }, + "validated_by": { + "type": "string", + "description": "GitHub handle of the contributor who validated this entry." + }, + "validated_on": { + "type": "string", + "format": "date", + "description": "ISO-8601 date of the validation run." + }, + "report_id": { + "type": "string", + "description": "Optional unique identifier linking back to a CI run, JIRA ticket, or install-report.json." + } + } + } + }, + + "schema_version": "1.0.0", + "last_updated": "2026-04-23", + "driver_versions_covered": ["1.0.6.1"], + "entries": [ + { + "distro_id": "ubuntu", + "distro_version": "22.04", + "kernel_range": "5.15..6.8", + "arch": ["x86_64", "amd64"], + "status": "pass", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-generic"], + "notes": "Primary development target. Secure Boot requires one-time MOK enrollment; DKMS will prompt during install.", + "validated_by": "qualcomm", + "validated_on": "2026-04-23" + }, + { + "distro_id": "ubuntu", + "distro_version": "24.04", + "kernel_range": "6.5..6.11", + "arch": ["x86_64", "amd64", "arm64", "aarch64"], + "status": "pass", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-generic"], + "notes": "LTS. Validated on 6.8 and 6.11 HWE kernels.", + "validated_by": "qualcomm", + "validated_on": "2026-04-23" + }, + { + "distro_id": "debian", + "distro_version": "12", + "kernel_range": "6.1..6.12", + "arch": ["x86_64", "amd64", "arm64", "aarch64"], + "status": "pass", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-amd64"], + "notes": "Use 'linux-headers-arm64' on ARM systems.", + "validated_by": "qualcomm", + "validated_on": "2026-04-23" + }, + { + "distro_id": "debian", + "distro_version": "11", + "kernel_range": "5.10..5.10", + "arch": ["x86_64", "amd64"], + "status": "partial", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "linux-headers-amd64"], + "notes": "dh-dkms is not available; build-deb.sh falls back to plain dh_dkms invocation. Some newer features unavailable.", + "validated_by": "qualcomm", + "validated_on": "2026-04-23" + }, + { + "distro_id": "linuxmint", + "distro_version": "21.*", + "kernel_range": "5.15..6.8", + "arch": ["x86_64", "amd64"], + "status": "pass", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-generic"], + "notes": "Behaves as Ubuntu 22.04." + }, + { + "distro_id": "pop", + "distro_version": "22.04", + "kernel_range": "6.2..6.9", + "arch": ["x86_64", "amd64"], + "status": "pass", + "secure_boot": "supported", + "required_packages": ["dkms", "debhelper", "dpkg-dev", "dh-dkms", "linux-headers-generic"], + "notes": "Pop!_OS ships a System76-signed kernel; DKMS modules load automatically once MOK is enrolled." + }, + { + "distro_id": "fedora", + "distro_version": "40", + "kernel_range": "6.8..6.10", + "arch": ["x86_64", "aarch64"], + "status": "untested", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "kernel-devel", "kernel-headers", "make", "gcc"], + "notes": "Deb packaging does not apply. Use the Makefile under src/linux or build an RPM (future work)." + }, + { + "distro_id": "fedora", + "distro_version": "41", + "kernel_range": "6.11..*", + "arch": ["x86_64", "aarch64"], + "status": "untested", + "secure_boot": "manual-mok-required", + "immutable_rootfs": false, + "notes": "See Fedora 40 entry." + }, + { + "distro_id": "fedora", + "distro_version": "*", + "kernel_range": "*..*", + "arch": ["x86_64", "aarch64"], + "status": "untested", + "immutable_rootfs": true, + "notes": "Silverblue / Kinoite / IoT editions: requires rpm-ostree layering or a toolbox container. Not supported out-of-the-box." + }, + { + "distro_id": "rhel", + "distro_version": "9", + "kernel_range": "5.14..5.14", + "arch": ["x86_64", "aarch64"], + "status": "untested", + "secure_boot": "manual-mok-required", + "required_packages": ["dkms", "kernel-devel", "kernel-headers", "make", "gcc"], + "notes": "dkms is available via EPEL. Deb packaging does not apply." + }, + { + "distro_id": "centos", + "distro_version": "9", + "kernel_range": "5.14..*", + "arch": ["x86_64", "aarch64"], + "status": "partial", + "notes": "Crash reported in RMNET driver on Fedora/CentOS (QUD-1629). Tracking fix." + }, + { + "distro_id": "arch", + "distro_version": "*", + "kernel_range": "6.0..*", + "arch": ["x86_64"], + "status": "untested", + "notes": "Rolling release. Use the Makefile path; deb packaging does not apply." + } + ] +} \ No newline at end of file diff --git a/scripts/match_known_good.py b/scripts/match_known_good.py new file mode 100755 index 0000000..6e1d9a9 --- /dev/null +++ b/scripts/match_known_good.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause-Clear +"""Match a detected Linux config against known-good-configs.json. + +Invocation: + match_known_good.py + + +On stdout, prints three tab-separated fields on one line: + \t\t + +Where is one of: pass | partial | fail | untested | unknown. +"unknown" means no entry matched at all. + +Exit status is always 0 unless arguments are malformed; the classifier +status is communicated via the printed line so the caller can decide how +to weight it. +""" +from __future__ import annotations + +import fnmatch +import json +import sys + + +def ver_tuple(v: str) -> tuple[int, ...]: + out: list[int] = [] + for p in str(v).split("."): + try: + out.append(int(p)) + except ValueError: + out.append(0) + return tuple(out) if out else (0,) + + +def in_range(kmm: str, rng: str | None) -> bool: + """Return True if kmm (e.g. '6.8') is within the inclusive 'min..max' + range specified by rng. '*' on either side means unbounded.""" + if not rng or rng in ("*", "*..*"): + return True + if ".." not in rng: + # Exact match + return kmm == rng + lo, hi = rng.split("..", 1) + kt = ver_tuple(kmm) + if lo and lo != "*": + if kt < ver_tuple(lo): + return False + if hi and hi != "*": + if kt > ver_tuple(hi): + return False + return True + + +def arch_matches(detected: str, entry_archs: list[str]) -> bool: + if not entry_archs: + return True + # Treat common synonyms as equivalent. + synonyms = { + "x86_64": {"x86_64", "amd64"}, + "amd64": {"x86_64", "amd64"}, + "aarch64": {"aarch64", "arm64"}, + "arm64": {"aarch64", "arm64"}, + } + det_set = synonyms.get(detected, {detected}) + for a in entry_archs: + if a in det_set: + return True + return False + + +def main(argv: list[str]) -> int: + if len(argv) != 6: + print( + "usage: match_known_good.py ", + file=sys.stderr, + ) + return 2 + + path, did, dver, kmm, arch = argv[1:6] + try: + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + except (OSError, json.JSONDecodeError) as exc: + print(f"unknown\tfailed to read configs: {exc}\t") + return 0 + + entries = data.get("entries", []) + best = None + for e in entries: + e_id = e.get("distro_id", "") + if e_id != did and e_id != "*": + continue + e_ver = e.get("distro_version", "*") + if e_ver != "*" and not fnmatch.fnmatchcase(dver, e_ver): + continue + if not in_range(kmm, e.get("kernel_range")): + continue + if not arch_matches(arch, e.get("arch", [])): + continue + # Prefer exact distro_version matches over wildcard ones. + specificity = 0 + if e_ver == dver: + specificity += 2 + elif "*" not in e_ver: + specificity += 1 + if best is None or specificity > best[0]: + best = (specificity, e) + + if best is None: + print("unknown\tNo entry in known-good-configs.json matches this system.\t") + return 0 + + e = best[1] + status = e.get("status", "untested") + notes = e.get("notes", "") + req = ",".join(e.get("required_packages", []) or []) + # Squash tabs/newlines in notes so the one-line protocol is preserved. + notes = notes.replace("\t", " ").replace("\n", " ").replace("\r", " ") + print(f"{status}\t{notes}\t{req}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) \ No newline at end of file