78 lines
2.7 KiB
Python
Executable File
78 lines
2.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Зонд конфиг-канала GANSS ARDOR_Katana (0c45:8006) через hidraw ioctl.
|
|
|
|
HIDIOCGFEATURE(len) = _IOWR('H', 0x07, len) — размер кодируется в номере ioctl.
|
|
Для состояния клавиатуры зонд безопасен: только GET_FEATURE-запросы,
|
|
ничего не записывается.
|
|
|
|
Использование: misc/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())
|