diff --git a/Makefile b/Makefile deleted file mode 100644 index 3096596..0000000 --- a/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -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 d5e8064..0eb09c7 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,54 @@ Device Driver -============ +====================== -This folder contains a minimal **out-of-tree** Linux kernel module that acts as a USB **boot-protocol mouse** driver. +A collection of USB device drivers for Linux kernel, demonstrating how to interact with various USB HID devices. -It supports: -- Left and right buttons -- Scroll wheel -- Relative cursor motion (X/Y) +## Current Drivers -Important notes ---------------- +### Mouse Driver (`mouse/`) +A USB HID mouse driver that supports 16-bit coordinate tracking for high-DPI gaming mice. Tested with Cooler Master MM710. -- 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: +**Features:** +- Left, right, middle, side, and extra button support +- 16-bit X/Y movement (high-speed tracking) +- Scroll wheel support +- Binds to HID Boot Protocol Mouse interfaces +**Building:** ```bash -cd Device-Driver +cd mouse/ make ``` -Load ----- +## USB Driver Manager +The `usb_driver_manager.py` tool simplifies the process of binding USB devices to custom drivers. + +**Usage:** ```bash -sudo insmod usb_bootmouse.ko -dmesg | tail -n 50 +# Search for .ko files in current directory +sudo python3 usb_driver_manager.py + +# Search in specific directories +sudo python3 usb_driver_manager.py ./mouse ./keyboard + +# The tool will: +# 1. List available USB HID devices +# 2. Show available kernel modules (.ko files) +# 3. Unbind the device from its current driver +# 4. Unload existing module (if already loaded) +# 5. Load the new module +# 6. Bind the device to the new driver ``` -If you want to restrict binding to a specific device: +## Future Drivers -```bash -sudo insmod usb_bootmouse.ko match_vendor=0x046d match_product=0xc077 -``` +- **Keyboard**: USB HID keyboard driver +- **Racing Wheel**: USB racing wheel driver with force feedback -(Replace IDs with your mouse vendor/product from `lsusb`.) +## Requirements -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 -``` +- Linux kernel headers +- Python 3.6+ +- Root/sudo access for driver loading and binding diff --git a/bind_usb_bootmouse.sh b/bind_usb_bootmouse.sh deleted file mode 100755 index 2a15e8a..0000000 --- a/bind_usb_bootmouse.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/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 deleted file mode 100755 index fbb73a8..0000000 --- a/restore_hid.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/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 deleted file mode 100644 index 9fcc3d9..0000000 --- a/usb_bootmouse.c +++ /dev/null @@ -1,324 +0,0 @@ -// 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 a1911f6..8e44dda 100755 --- a/usb_driver_manager.py +++ b/usb_driver_manager.py @@ -14,6 +14,7 @@ import subprocess import glob import re import time +import argparse from pathlib import Path @@ -208,36 +209,50 @@ def display_usb_devices(devices, filter_input=True): return devices -def get_kernel_modules(directory="."): +def get_kernel_modules(directories=None): """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 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) + # 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 - # 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 + 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) - modules.append({ - 'name': module_name, - 'path': module_path, - 'description': description - }) - except Exception: - modules.append({ - 'name': module_name, - 'path': module_path, - 'description': "No description available" - }) + # 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" + }) return modules @@ -485,8 +500,38 @@ 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") @@ -525,7 +570,9 @@ 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 - modules = get_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) displayed_modules = display_kernel_modules(modules) if not displayed_modules: @@ -540,14 +587,24 @@ 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']}") - print(f" 2. Load module {selected_module['name']}") - print(f" 3. Bind interface(s) to the new 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") confirm = input(f"\n{Colors.BOLD}Proceed? (yes/no): {Colors.END}").strip().lower() if confirm not in ['yes', 'y']: @@ -562,6 +619,15 @@ 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...")