From 8ab4ca75337a702e95ad1e607f332b1218f56d9c Mon Sep 17 00:00:00 2001 From: Thomas Hilscher Date: Fri, 9 Jan 2026 17:22:38 +0100 Subject: [PATCH] Added first tries --- Makefile | 31 ++++ README.md | 115 ++++++++++++++- bind_usb_bootmouse.sh | 111 +++++++++++++++ restore_hid.sh | 64 +++++++++ usb_bootmouse.c | 324 ++++++++++++++++++++++++++++++++++++++++++ usb_driver_manager.py | 122 ++++------------ 6 files changed, 671 insertions(+), 96 deletions(-) create mode 100644 Makefile create mode 100755 bind_usb_bootmouse.sh create mode 100755 restore_hid.sh create mode 100644 usb_bootmouse.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3096596 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +obj-m += usb_bootmouse.o + +KVER := $(shell uname -r) + +# Most distros expose headers at /lib/modules/$(KVER)/build, but Arch/Manjaro +# often uses /usr/lib/modules/$(KVER)/build. +KDIR ?= $(firstword \ + $(wildcard /lib/modules/$(KVER)/build) \ + $(wildcard /usr/lib/modules/$(KVER)/build)) + +PWD := $(shell pwd) + +OUT := $(PWD)/out + +all: + @if [ -z "$(KDIR)" ]; then \ + echo "ERROR: kernel build dir not found for $(KVER). Install kernel headers (e.g. linux-headers)"; \ + exit 2; \ + fi + mkdir -p $(OUT) + $(MAKE) -C $(KDIR) M=$(PWD) modules + -mv -f -- *.ko *.mod.c *.o *.mod modules.order .*.cmd $(OUT) 2>/dev/null || true + @echo "Build outputs moved to $(OUT)" + +clean: + @if [ -z "$(KDIR)" ]; then \ + echo "ERROR: kernel build dir not found for $(KVER)."; \ + exit 2; \ + fi + $(MAKE) -C $(KDIR) M=$(PWD) clean + @rm -rf $(OUT) diff --git a/README.md b/README.md index 8124b1f..d5e8064 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,116 @@ Device Driver -============= +============ -[Logitech G29 USB Protocol](logitech-G29.md) +This folder contains a minimal **out-of-tree** Linux kernel module that acts as a USB **boot-protocol mouse** driver. + +It supports: +- Left and right buttons +- Scroll wheel +- Relative cursor motion (X/Y) + +Important notes +--------------- + +- This is an **educational** example. Real USB mice are normally handled by the kernel HID stack (e.g. `usbhid` / `hid-generic`). +- This driver binds to HID **Boot Mouse** interfaces (class=HID, subclass=BOOT, protocol=MOUSE). Many mice work, but not all. +- To use it on a running system you typically must **unbind** the existing driver from that USB interface first. + +Files +----- + +- `usb_bootmouse.c` – kernel module (USB driver + input device) +- `Makefile` – builds against your running kernel headers + +Build +----- + +Install kernel headers/build deps (examples): + +- Debian/Ubuntu: `sudo apt-get install build-essential linux-headers-$(uname -r)` +- Fedora: `sudo dnf install @development-tools kernel-devel-$(uname -r)` + +Then build: + +```bash +cd Device-Driver +make +``` + +Load +---- + +```bash +sudo insmod usb_bootmouse.ko +dmesg | tail -n 50 +``` + +If you want to restrict binding to a specific device: + +```bash +sudo insmod usb_bootmouse.ko match_vendor=0x046d match_product=0xc077 +``` + +(Replace IDs with your mouse vendor/product from `lsusb`.) + +Bind it to your mouse (unbind/bind) +--------------------------------- + +1) Find the USB interface path. + +You can use `dmesg` when plugging the mouse in, or inspect: + +```bash +ls -l /sys/bus/usb/devices/ +``` + +Typical interface names look like `1-2:1.0` (bus-port:config.interface). + +2) Unbind the existing HID driver (commonly `usbhid`) from that interface: + +```bash +DEVIF="1-2:1.0" # <- change this +echo -n "$DEVIF" | sudo tee /sys/bus/usb/drivers/usbhid/unbind +``` + +3) Bind this module to the interface: + +```bash +echo -n "$DEVIF" | sudo tee /sys/bus/usb/drivers/usb_bootmouse/bind +``` + +At this point, the driver should create an input device (via `evdev`). + +Test +---- + +List input devices and find the new one: + +```bash +cat /proc/bus/input/devices +``` + +Or use `evtest`: + +```bash +sudo apt-get install evtest # or your distro equivalent +sudo evtest +``` + +You should see events: +- `BTN_LEFT`, `BTN_RIGHT` +- `REL_X`, `REL_Y` +- `REL_WHEEL` + +Unload +------ + +```bash +sudo rmmod usb_bootmouse +``` + +If you want the original HID driver back, bind it again: + +```bash +echo -n "$DEVIF" | sudo tee /sys/bus/usb/drivers/usbhid/bind +``` diff --git a/bind_usb_bootmouse.sh b/bind_usb_bootmouse.sh new file mode 100755 index 0000000..2a15e8a --- /dev/null +++ b/bind_usb_bootmouse.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# bind_usb_bootmouse.sh +# Usage: sudo ./bind_usb_bootmouse.sh [VID] [PID] +# Example: sudo ./bind_usb_bootmouse.sh 0x2516 0x012f + +set -euo pipefail + +if [ "$EUID" -ne 0 ]; then + echo "This script must be run as root (sudo)." >&2 + exit 1 +fi + +VID_IN=${1:-0x2516} +PID_IN=${2:-0x012f} + +# normalize (strip 0x, lowercase, pad to 4 hex digits) +norm_hex() { + local v=${1,,} + v=${v#0x} + printf "%04x" $((16#$v)) +} + +VID=$(norm_hex "$VID_IN") +PID=$(norm_hex "$PID_IN") + +MAPFILE="/var/run/usb_bootmouse_bind_${VID}_${PID}.map" + +echo "Looking for USB device VID=$VID PID=$PID..." + +found=0 +for d in /sys/bus/usb/devices/*; do + if [ -f "$d/idVendor" ] && [ -f "$d/idProduct" ]; then + v=$(cat "$d/idVendor") + p=$(cat "$d/idProduct") + if [ "$v" = "$VID" ] && [ "$p" = "$PID" ]; then + found=1 + devdir="$d" + break + fi + fi +done + +if [ $found -ne 1 ]; then + echo "Device not found in sysfs for VID=$VID PID=$PID" >&2 + exit 2 +fi + +echo "Device found at $devdir" + +# Ensure our module is loaded +if ! lsmod | grep -q '^usb_bootmouse'; then + if ! modprobe usb_bootmouse 2>/dev/null; then + echo "Attempting to insmod from module file..." + if [ -f "$(pwd)/usb_bootmouse.ko" ]; then + insmod "$(pwd)/usb_bootmouse.ko" + else + echo "usb_bootmouse module not found. Build the module first." >&2 + exit 3 + fi + fi +fi + +echo "Module usb_bootmouse loaded" + +# Prepare map file +rm -f "$MAPFILE" +mkdir -p "$(dirname "$MAPFILE")" + +# Iterate over interfaces under the device (like 1-2:1.0) +for ifpath in "$devdir"/*:*; do + [ -e "$ifpath" ] || continue + IFNAME=$(basename "$ifpath") + + # find the current driver for this interface + orig_driver="" + for drv in /sys/bus/usb/drivers/*; do + [ -e "$drv/$IFNAME" ] || continue + orig_driver=$(basename "$drv") + break + done + + if [ -z "$orig_driver" ]; then + echo "Interface $IFNAME has no driver currently; will try to bind usb_bootmouse directly" + else + echo "Interface $IFNAME currently bound to driver '$orig_driver'" + # record original driver + echo "$IFNAME:$orig_driver" >> "$MAPFILE" + + # unbind original driver + echo -n "$IFNAME" > "/sys/bus/usb/drivers/$orig_driver/unbind" || { + echo "Failed to unbind $IFNAME from $orig_driver" >&2 + continue + } + echo "Unbound $IFNAME from $orig_driver" + fi + + # bind our driver + echo -n "$IFNAME" > /sys/bus/usb/drivers/usb_bootmouse/bind || { + echo "Failed to bind $IFNAME to usb_bootmouse" >&2 + continue + } + echo "Bound $IFNAME to usb_bootmouse" +done + +if [ -f "$MAPFILE" ]; then + echo "Saved mapping to $MAPFILE" +else + echo "No original driver mappings were saved (device may have had no drivers)." +fi + +exit 0 diff --git a/restore_hid.sh b/restore_hid.sh new file mode 100755 index 0000000..fbb73a8 --- /dev/null +++ b/restore_hid.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# restore_hid.sh +# Usage: sudo ./restore_hid.sh [VID] [PID] +# Restores original driver bindings saved by bind_usb_bootmouse.sh + +set -euo pipefail + +if [ "$EUID" -ne 0 ]; then + echo "This script must be run as root (sudo)." >&2 + exit 1 +fi + +VID_IN=${1:-0x2516} +PID_IN=${2:-0x012f} + +norm_hex() { + local v=${1,,} + v=${v#0x} + printf "%04x" $((16#$v)) +} + +VID=$(norm_hex "$VID_IN") +PID=$(norm_hex "$PID_IN") + +MAPFILE="/var/run/usb_bootmouse_bind_${VID}_${PID}.map" + +if [ ! -f "$MAPFILE" ]; then + echo "Mapping file $MAPFILE not found. Nothing to restore." >&2 + exit 2 +fi + +echo "Restoring bindings from $MAPFILE" + +while IFS=: read -r IFNAME ORIG; do + [ -n "$IFNAME" ] || continue + [ -n "$ORIG" ] || continue + + # unbind our driver if it is bound + if [ -e "/sys/bus/usb/drivers/usb_bootmouse/$IFNAME" ]; then + echo -n "$IFNAME" > /sys/bus/usb/drivers/usb_bootmouse/unbind || { + echo "Failed to unbind $IFNAME from usb_bootmouse" >&2 + } + echo "Unbound $IFNAME from usb_bootmouse" + else + echo "usb_bootmouse not bound to $IFNAME" + fi + + # bind the original driver back + if [ -d "/sys/bus/usb/drivers/$ORIG" ]; then + echo -n "$IFNAME" > /sys/bus/usb/drivers/$ORIG/bind || { + echo "Failed to bind $IFNAME to $ORIG" >&2 + continue + } + echo "Bound $IFNAME to $ORIG" + else + echo "Original driver $ORIG not present on this system; skipping bind for $IFNAME" >&2 + fi + +done < "$MAPFILE" + +rm -f "$MAPFILE" + +echo "Restoration complete." +exit 0 diff --git a/usb_bootmouse.c b/usb_bootmouse.c new file mode 100644 index 0000000..9fcc3d9 --- /dev/null +++ b/usb_bootmouse.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "usb_bootmouse" + +static ushort match_vendor; +module_param(match_vendor, ushort, 0444); +MODULE_PARM_DESC(match_vendor, "If non-zero, only bind to this USB vendor id (hex or dec)"); + +static ushort match_product; +module_param(match_product, ushort, 0444); +MODULE_PARM_DESC(match_product, "If non-zero, only bind to this USB product id (hex or dec)"); + +/* Runtime tuning parameters for devices with nonstandard report layouts */ +static int force_offset = -1; /* -1 = auto, 0 = no report-id, 1 = report-id present */ +module_param(force_offset, int, 0444); +MODULE_PARM_DESC(force_offset, "Force report-data offset (0 or 1). -1 = autodetect"); + +static bool swap_xy = false; +module_param(swap_xy, bool, 0444); +MODULE_PARM_DESC(swap_xy, "Swap X/Y axes (try true if horizontal/vertical are inverted)"); + +static int wheel_index = -1; /* -1 = autodetect (3+off) */ +module_param(wheel_index, int, 0444); +MODULE_PARM_DESC(wheel_index, "Force wheel byte index in report (0-based). -1 = autodetect"); + +struct bootmouse { + struct usb_device *udev; + struct usb_interface *intf; + struct input_dev *input; + + struct urb *irq_urb; + u8 *irq_data; + dma_addr_t irq_dma; + unsigned int irq_len; + unsigned int irq_interval; + unsigned int irq_ep; + + char phys[64]; + atomic_t opened; +}; + +static void bootmouse_irq(struct urb *urb) +{ + struct bootmouse *m = urb->context; + int status = urb->status; + u8 *data; + s8 dx, dy, wheel; + + if (!m) + return; + + if (!atomic_read(&m->opened)) + return; + + if (status) { + /* + * -ENOENT/-ECONNRESET typically come from disconnect/kill/unlink. + * Avoid noisy logs; just stop resubmitting. + */ + if (status == -ENOENT || status == -ECONNRESET || status == -ESHUTDOWN) + return; + + dev_dbg(&m->intf->dev, "irq urb status %d\n", status); + goto resubmit; + } + + data = m->irq_data; + + /* + * Many HID mice use the Boot protocol layout: + * [0]=buttons [1]=dx [2]=dy [3]=wheel + * Some devices prepend a report-id, shifting fields: + * [0]=rid [1]=buttons [2]=dx [3]=dy ... + * Use a small heuristic to pick the correct offset so axes don't get swapped. + */ + { + int off = 0; + int sA = 0, sB = 0; + /* score no-report-id layout: sum(|dx|+|dy|) at indexes 1,2 */ + if (m->irq_len >= 3) + sA = abs((s8)data[1]) + abs((s8)data[2]); + /* score report-id layout: sum at indexes 2,3 */ + if (m->irq_len >= 4) + sB = abs((s8)data[2]) + abs((s8)data[3]); + /* choose offset=1 if sB noticeably larger than sA */ + if (sB > sA * 2) + off = 1; + + /* buttons */ + input_report_key(m->input, BTN_LEFT, !!(data[0 + off] & 0x01)); + input_report_key(m->input, BTN_RIGHT, !!(data[0 + off] & 0x02)); + + /* motion */ + dx = (s8)data[1 + off]; + dy = (s8)data[2 + off]; + if (swap_xy) { + s8 tmp = dx; dx = dy; dy = tmp; + } + input_report_rel(m->input, REL_X, dx); + input_report_rel(m->input, REL_Y, dy); + + /* wheel if present -- allow override */ + if (wheel_index >= 0) { + if ((unsigned)wheel_index < m->irq_len) { + wheel = (s8)data[wheel_index]; + input_report_rel(m->input, REL_WHEEL, wheel); + } + } else if (m->irq_len >= 4 + off) { + wheel = (s8)data[3 + off]; + input_report_rel(m->input, REL_WHEEL, wheel); + } + } + + input_sync(m->input); + +resubmit: + usb_submit_urb(m->irq_urb, GFP_ATOMIC); +} + +static int bootmouse_open(struct input_dev *dev) +{ + struct bootmouse *m = input_get_drvdata(dev); + int ret; + + if (!m) + return -ENODEV; + + atomic_set(&m->opened, 1); + + ret = usb_submit_urb(m->irq_urb, GFP_KERNEL); + if (ret) { + atomic_set(&m->opened, 0); + return ret; + } + + return 0; +} + +static void bootmouse_close(struct input_dev *dev) +{ + struct bootmouse *m = input_get_drvdata(dev); + + if (!m) + return; + + atomic_set(&m->opened, 0); + usb_kill_urb(m->irq_urb); +} + +static int bootmouse_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *epd = NULL; + struct bootmouse *m; + struct input_dev *input; + int i, error; + + if (match_vendor && le16_to_cpu(udev->descriptor.idVendor) != match_vendor) + return -ENODEV; + if (match_product && le16_to_cpu(udev->descriptor.idProduct) != match_product) + return -ENODEV; + + alts = intf->cur_altsetting; + for (i = 0; i < alts->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *d = &alts->endpoint[i].desc; + + if (usb_endpoint_is_int_in(d)) { + epd = d; + break; + } + } + if (!epd) + return -ENODEV; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + + m->udev = usb_get_dev(udev); + m->intf = intf; + atomic_set(&m->opened, 0); + + m->irq_ep = usb_endpoint_num(epd); + m->irq_len = usb_endpoint_maxp(epd); + m->irq_interval = epd->bInterval; + + m->irq_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!m->irq_urb) { + error = -ENOMEM; + goto err_free; + } + + m->irq_data = usb_alloc_coherent(udev, m->irq_len, GFP_KERNEL, &m->irq_dma); + if (!m->irq_data) { + error = -ENOMEM; + goto err_free_urb; + } + + input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_buf; + } + + m->input = input; + + usb_make_path(udev, m->phys, sizeof(m->phys)); + strlcat(m->phys, "/input0", sizeof(m->phys)); + + input->name = "USB Boot Mouse (example driver)"; + input->phys = m->phys; + usb_to_input_id(udev, &input->id); + input->dev.parent = &intf->dev; + + input->open = bootmouse_open; + input->close = bootmouse_close; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_REL, input->evbit); + + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + + __set_bit(REL_X, input->relbit); + __set_bit(REL_Y, input->relbit); + __set_bit(REL_WHEEL, input->relbit); + + input_set_drvdata(input, m); + + usb_fill_int_urb( + m->irq_urb, + udev, + usb_rcvintpipe(udev, epd->bEndpointAddress), + m->irq_data, + m->irq_len, + bootmouse_irq, + m, + m->irq_interval); + + m->irq_urb->transfer_dma = m->irq_dma; + m->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_set_intfdata(intf, m); + + error = input_register_device(input); + if (error) + goto err_clear_intfdata; + + dev_info(&intf->dev, + "bound to %04x:%04x, int-in ep 0x%02x maxp %u interval %u\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + epd->bEndpointAddress, + m->irq_len, + m->irq_interval); + + return 0; + +err_clear_intfdata: + usb_set_intfdata(intf, NULL); + input_free_device(input); + m->input = NULL; +err_free_buf: + usb_free_coherent(udev, m->irq_len, m->irq_data, m->irq_dma); +err_free_urb: + usb_free_urb(m->irq_urb); +err_free: + usb_put_dev(m->udev); + kfree(m); + return error; +} + +static void bootmouse_disconnect(struct usb_interface *intf) +{ + struct bootmouse *m = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (!m) + return; + + if (m->input) { + /* Triggers ->close if opened */ + input_unregister_device(m->input); + m->input = NULL; + } + + usb_kill_urb(m->irq_urb); + usb_free_coherent(m->udev, m->irq_len, m->irq_data, m->irq_dma); + usb_free_urb(m->irq_urb); + usb_put_dev(m->udev); + kfree(m); + + dev_info(&intf->dev, "disconnected\n"); +} + +/* Match HID Boot Mouse interfaces. */ +static const struct usb_device_id bootmouse_id_table[] = { + /* HID Boot Mouse: bInterfaceClass = 3 (HID), bInterfaceSubClass = 1 (BOOT), bInterfaceProtocol = 2 (MOUSE) */ + { USB_INTERFACE_INFO(3, 1, 2) }, + { } +}; +MODULE_DEVICE_TABLE(usb, bootmouse_id_table); + +static struct usb_driver bootmouse_driver = { + .name = DRV_NAME, + .probe = bootmouse_probe, + .disconnect = bootmouse_disconnect, + .id_table = bootmouse_id_table, +}; + +module_usb_driver(bootmouse_driver); + +MODULE_AUTHOR("Example / educational"); +MODULE_DESCRIPTION("Minimal USB HID boot-protocol mouse driver (buttons, motion, wheel)"); +MODULE_LICENSE("GPL"); diff --git a/usb_driver_manager.py b/usb_driver_manager.py index 8e44dda..a1911f6 100755 --- a/usb_driver_manager.py +++ b/usb_driver_manager.py @@ -14,7 +14,6 @@ import subprocess import glob import re import time -import argparse from pathlib import Path @@ -209,50 +208,36 @@ def display_usb_devices(devices, filter_input=True): return devices -def get_kernel_modules(directories=None): +def get_kernel_modules(directory="."): """Get list of available kernel modules (.ko files)""" - if directories is None: - directories = ["."] - modules = [] - seen_modules = set() # Track module names to avoid duplicates - # Search for .ko files in each specified directory - for directory in directories: - if not os.path.exists(directory): - print_warning(f"Directory not found: {directory}") - continue + # Search for .ko files in the specified directory + for ko_file in glob.glob(os.path.join(directory, "*.ko")): + module_name = os.path.basename(ko_file) + module_path = os.path.abspath(ko_file) - for ko_file in glob.glob(os.path.join(directory, "*.ko")): - module_name = os.path.basename(ko_file) - module_path = os.path.abspath(ko_file) + # Get module info if possible + try: + result = subprocess.run(['modinfo', module_path], + capture_output=True, text=True) + description = "No description" + for line in result.stdout.splitlines(): + if line.startswith("description:"): + description = line.split(":", 1)[1].strip() + break - # Skip duplicates (same module name already found) - if module_name in seen_modules: - continue - seen_modules.add(module_name) - - # Get module info if possible - try: - result = subprocess.run(['modinfo', module_path], - capture_output=True, text=True) - description = "No description" - for line in result.stdout.splitlines(): - if line.startswith("description:"): - description = line.split(":", 1)[1].strip() - break - - modules.append({ - 'name': module_name, - 'path': module_path, - 'description': description - }) - except Exception: - modules.append({ - 'name': module_name, - 'path': module_path, - 'description': "No description available" - }) + modules.append({ + 'name': module_name, + 'path': module_path, + 'description': description + }) + except Exception: + modules.append({ + 'name': module_name, + 'path': module_path, + 'description': "No description available" + }) return modules @@ -500,38 +485,8 @@ def unload_module(module): return False -def is_module_loaded(module): - """Check if a kernel module is currently loaded""" - driver_name = get_module_driver_name(module) - try: - result = subprocess.run(['lsmod'], capture_output=True, text=True) - for line in result.stdout.splitlines(): - if line.split()[0] == driver_name: - return True - return False - except Exception: - return False - - def main(): """Main function""" - # Parse command line arguments - parser = argparse.ArgumentParser( - description='USB Driver Manager - CLI tool for managing USB device driver bindings', - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog='Examples:\n' - ' sudo %(prog)s # Search for .ko files in current directory\n' - ' sudo %(prog)s /path/to/modules # Search in specific directory\n' - ' sudo %(prog)s . /path/to/dir2 # Search in multiple directories\n' - ) - parser.add_argument( - 'directories', - nargs='*', - default=['.'], - help='Directories to search for kernel modules (.ko files). Defaults to current directory.' - ) - args = parser.parse_args() - print(f"{Colors.BOLD}{Colors.CYAN}") print("=" * 60) print(" USB Driver Manager") @@ -570,9 +525,7 @@ def main(): print(f" {iface['name']}: class={iface['class']} subclass={iface['subclass']} protocol={iface['protocol']} driver={driver_color}{iface['driver']}{Colors.END}") # Step 3: List available kernel modules - if len(args.directories) > 1 or args.directories[0] != '.': - print(f"\nSearching for kernel modules in: {', '.join(args.directories)}") - modules = get_kernel_modules(args.directories) + modules = get_kernel_modules() displayed_modules = display_kernel_modules(modules) if not displayed_modules: @@ -587,24 +540,14 @@ def main(): selected_module = displayed_modules[module_idx] print(f"\n{Colors.CYAN}Selected module: {selected_module['name']}{Colors.END}") - # Check if module is already loaded - module_already_loaded = is_module_loaded(selected_module) - if module_already_loaded: - print_warning(f"Module {selected_module['name']} is already loaded and will be reloaded.") - # Step 5: Confirm operation print(f"\n{Colors.YELLOW}This will:{Colors.END}") if selected_device.get('interfaces'): print(f" 1. Unbind interface(s) from current driver(s)") else: print(f" 1. Unbind {selected_device['name']} from {selected_device['driver']}") - if module_already_loaded: - print(f" 2. Unload existing module {selected_module['name']}") - print(f" 3. Load module {selected_module['name']} (fresh version)") - print(f" 4. Bind interface(s) to the new driver") - else: - print(f" 2. Load module {selected_module['name']}") - print(f" 3. Bind interface(s) to the new driver") + print(f" 2. Load module {selected_module['name']}") + print(f" 3. Bind interface(s) to the new driver") confirm = input(f"\n{Colors.BOLD}Proceed? (yes/no): {Colors.END}").strip().lower() if confirm not in ['yes', 'y']: @@ -619,15 +562,6 @@ def main(): print_error("Failed to unbind device. Aborting.") sys.exit(1) - # Unload module if already loaded - if module_already_loaded: - if not unload_module(selected_module): - print_error("Failed to unload existing module.") - print_warning("You may need to manually unbind all devices using this driver first.") - sys.exit(1) - # Give kernel a moment after unloading - time.sleep(0.3) - # Load new module if not load_module(selected_module): print_error("Failed to load module. Attempting to restore...")