69 lines
2.6 KiB
Python
Executable File
69 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import os
|
|
import pyudev
|
|
|
|
def generate_rules():
|
|
context = pyudev.Context()
|
|
|
|
# Find all TTY devices driven by usb-serial drivers (CP210x, FTDI, etc.)
|
|
devices = []
|
|
for device in context.list_devices(subsystem='tty'):
|
|
if 'ID_VENDOR_ID' in device.properties and 'ID_MODEL_ID' in device.properties:
|
|
# We filter for USB serial devices only
|
|
parent = device.find_parent('usb', 'usb_interface')
|
|
if parent:
|
|
devices.append(device)
|
|
|
|
# Sort by their physical path so they are ordered by hub port
|
|
# The 'DEVPATH' usually looks like .../usb1/1-2/1-2.3/1-2.3.4...
|
|
devices.sort(key=lambda x: x.properties.get('DEVPATH', ''))
|
|
|
|
print(f"# Detected {len(devices)} devices. Generating rules...\n")
|
|
|
|
rules = []
|
|
hub_counter = 1
|
|
port_counter = 1
|
|
last_parent_path = None
|
|
|
|
for dev in devices:
|
|
# Get the unique physical path identifier (KERNELS)
|
|
# We need the parent USB interface kernel name (e.g., '1-1.2:1.0')
|
|
parent = dev.find_parent('usb', 'usb_interface')
|
|
if not parent: continue
|
|
|
|
kernels_path = parent.device_path.split('/')[-1]
|
|
|
|
# Simple heuristic to detect a new hub (big jump in path length or numbering)
|
|
# You might need to manually tweak the hub numbers in the file later.
|
|
|
|
# Generate the rule
|
|
# KERNELS matches the physical port.
|
|
# SYMLINK creates the static alias.
|
|
rule = f'SUBSYSTEM=="tty", KERNELS=="{kernels_path}", SYMLINK+="esp_port_{len(rules)+1:02d}"'
|
|
rules.append(rule)
|
|
print(f"Mapped {dev.device_node} ({kernels_path}) -> /dev/esp_port_{len(rules):02d}")
|
|
|
|
# Write to file
|
|
with open("99-esp32-static.rules", "w") as f:
|
|
f.write("# Persistent USB Serial mapping for ESP32 Hubs\n")
|
|
f.write("# Generated automatically. Do not edit unless topology changes.\n\n")
|
|
for r in rules:
|
|
f.write(r + "\n")
|
|
|
|
print(f"\n{'-'*60}")
|
|
print(f"SUCCESS: Generated {len(rules)} rules in '99-esp32-static.rules'.")
|
|
print(f"To install:\n 1. Review the file to ensure order is correct.")
|
|
print(f" 2. sudo cp 99-esp32-static.rules /etc/udev/rules.d/")
|
|
print(f" 3. sudo udevadm control --reload-rules")
|
|
print(f" 4. sudo udevadm trigger")
|
|
print(f"{'-'*60}")
|
|
|
|
if __name__ == '__main__':
|
|
# Requires 'pyudev'. Install with: sudo dnf install python3-pyudev (or pip install pyudev)
|
|
try:
|
|
import pyudev
|
|
generate_rules()
|
|
except ImportError:
|
|
print("Error: This script requires 'pyudev'.")
|
|
print("Install it via: pip install pyudev")
|