#!/usr/bin/env python3 """Зонд конфиг-канала GANSS ARDOR_Katana (0c45:8006) через hidraw ioctl. HIDIOCGFEATURE(len) = _IOWR('H', 0x07, len) — размер кодируется в номере ioctl. Для состояния клавиатуры зонд безопасен: только GET_FEATURE-запросы, ничего не записывается. Использование: probe.py [hidrawN] (по умолчанию — автоопределение всех hidraw-узлов клавиатуры) """ import array import fcntl import glob import os import struct import sys # размеры буфера, которые пробуем по очереди для каждого report id PROBE_SIZES = (33, 65, 21, 17) def hid_iocgfeature(size: int) -> int: """Номер ioctl _IOWR('H', 0x07, size) для чтения feature-репорта.""" return (3 << 30) | (size << 16) | (0x48 << 8) | 0x07 def find_katana_hidraws() -> list[str]: """Имена всех hidraw-узлов клавиатуры (0C45:8006).""" links = glob.glob("/sys/bus/hid/devices/0003:0C45:8006.*/hidraw/hidraw*") return sorted(os.path.basename(link) for link in links) def read_report_id(device_path: str, report_id: int, size: int): """Прочитать feature-репорт; None при ошибке (нет такого rid и т.п.).""" fd = os.open(device_path, os.O_RDWR) try: buf = array.array("B", bytes(1 + size)) # первый байт = report id buf[0] = report_id res = fcntl.ioctl(fd, hid_iocgfeature(len(buf)), buf) n = struct.unpack("i", res[:4])[0] # ioctl возвращает [count, данные] return bytes(res[4:4 + n]) except OSError: return None finally: os.close(fd) def hexs(data: bytes) -> str: """Байты в строку вида 'aa bb cc'.""" return " ".join(f"{x:02x}" for x in data) def main(): targets = [sys.argv[1]] if len(sys.argv) > 1 else find_katana_hidraws() if not targets: raise SystemExit("GANSS ARDOR_Katana hidraw not found") for name in targets: path = f"/dev/{name}" print(f"=== {path} ===") for rid in range(0x01, 0x09): found = None for size in PROBE_SIZES: data = read_report_id(path, rid, size) if data: found = (size, data) break if found: size, data = found print(f" GET_FEATURE id=0x{rid:02x} req_size={size}: " f"{hexs(data)} ({len(data)} bytes)") else: print(f" GET_FEATURE id=0x{rid:02x}: empty") if __name__ == "__main__": sys.exit(main())