mirror of
https://github.com/derv82/wifit3.git
synced 2026-10-14 05:20:55 +08:00
95 lines
3.7 KiB
Python
95 lines
3.7 KiB
Python
"""Shared dev-tooling helpers, importable by any script under scripts/.
|
|
|
|
Currently: interface selection. Pick one plugged-in card by a name substring or an exact bus:addr,
|
|
so several cards can be connected at once and each script still targets the right one.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def _emit(log, msg: str) -> None:
|
|
(log or (lambda m: print(m, file=sys.stderr)))(msg)
|
|
|
|
|
|
def _parse_instance(instance: str) -> tuple[int, int] | None:
|
|
"""Parse "BUS:ADDR" (decimal, as pyusb reports dev.bus/dev.address) to (bus, addr)."""
|
|
try:
|
|
bus, _, addr = instance.partition(":")
|
|
return int(bus), int(addr)
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def select_device(ifaces, card: str = "", *, instance: str = "", log=None):
|
|
"""Select one wifit3 (USB) interface: exact ``instance`` "BUS:ADDR" wins (the only way to tell two
|
|
identical VID:PIDs apart), else a ``card`` name/description substring, else ifaces[0]; None
|
|
(roster printed) if a non-blank instance/card matches nothing."""
|
|
if instance:
|
|
want = _parse_instance(instance)
|
|
matches = [i for i in ifaces if (i.bus, i.address) == want] if want else []
|
|
if not matches:
|
|
roster = ", ".join(f"{i.bus}:{i.address} ({i.description})" for i in ifaces)
|
|
_emit(log, f"[-] no card at instance '{instance}'. Found: {roster}")
|
|
return None
|
|
return matches[0]
|
|
if not card:
|
|
if len(ifaces) > 1:
|
|
_emit(log, f"[!] {len(ifaces)} interfaces; using {ifaces[0].name}. "
|
|
f"Pass --card <substr> to pick another.")
|
|
return ifaces[0] if ifaces else None
|
|
matches = [i for i in ifaces if card.lower() in f"{i.name} {i.description}".lower()]
|
|
if not matches:
|
|
roster = ", ".join(f"{i.name} ({i.description})" for i in ifaces)
|
|
_emit(log, f"[-] no card matches '{card}'. Found: {roster}")
|
|
return None
|
|
if len(matches) > 1:
|
|
_emit(log, f"[!] '{card}' matched {len(matches)}; using {matches[0].name}.")
|
|
return matches[0]
|
|
|
|
|
|
def list_wireless_ifaces() -> list[tuple[str, str | None]]:
|
|
"""[(ifname, kernel_driver_or_None)] for every 802.11 netdev (has phy80211/)."""
|
|
base = Path("/sys/class/net")
|
|
out: list[tuple[str, str | None]] = []
|
|
if not base.exists():
|
|
return out
|
|
for p in sorted(base.iterdir()):
|
|
if not (p / "phy80211").exists():
|
|
continue
|
|
drv = None
|
|
link = p / "device" / "driver"
|
|
try:
|
|
if link.exists():
|
|
drv = os.path.basename(os.readlink(link))
|
|
except OSError:
|
|
pass
|
|
out.append((p.name, drv))
|
|
return out
|
|
|
|
|
|
def pick_kernel_iface(iface: str = "", card: str = "", *, log=None):
|
|
"""Pick one Linux 802.11 netdev (the kernel-driver side): explicit ``iface`` wins, else a ``card``
|
|
substring of "<ifname> <driver>", else the sole wireless iface; None (roster printed) on a miss
|
|
or an ambiguous choice."""
|
|
if iface:
|
|
return iface
|
|
ifaces = list_wireless_ifaces()
|
|
if card:
|
|
matches = [n for n, d in ifaces if card.lower() in f"{n} {d or ''}".lower()]
|
|
if not matches:
|
|
_emit(log, f"[-] no wireless iface matches '{card}'. Found: "
|
|
f"{', '.join(f'{n}({d})' for n, d in ifaces) or 'none'}")
|
|
return None
|
|
return matches[0]
|
|
if len(ifaces) == 1:
|
|
return ifaces[0][0]
|
|
if not ifaces:
|
|
_emit(log, "[-] no wireless interfaces found (is the card bound to a kernel driver?)")
|
|
return None
|
|
_emit(log, f"[!] {len(ifaces)} wireless ifaces: "
|
|
f"{', '.join(f'{n}({d})' for n, d in ifaces)}. Pass --iface or --card.")
|
|
return None
|