45 KiB
GANSS ARDOR_Katana (0c45:8006) — протокол и состояние реверса
Файл содержит ВСЁ необходимое для продолжения работы: протокол, карту клавиш, открытые вопросы, методику. Обновлять по мере находок. Пользовательская документация (установка, команды, режимы, свои скрипты) — по ссылкам из README.md.
Устройство
- Клавиатура GANSS ARDOR_Katana, USB
0C45:8006(Sonix SN32), 2 HID-интерфейса. - iface0 = boot keyboard + конфиг-канал (feature-репорты rid=0, 64 байта).
- iface1 = consumer/mouse + vendor page 0xFF00 rid=5 (только input, не используется).
- udev-правило
70-ganss-katana.rulesустановлено (доступ к hidraw без root). - hidraw-узел iface0 ищется через
/sys/class/hidraw/*/device(hid_dev, родитель*:1.0) + modaliashid:b0003g0001v00000C45p00008006.
Транспорт (реализовано в katana.py)
Feature-репорты через hidraw ioctl (HIDIOCSFEATURE/HIDIOCGFEATURE), буфер 65 байт: первый байт = report id (0).
usbhid не отцепляется — печать никогда не прерывается.
pyusb-вариант НЕ использовать (отцеплял драйвер).
macOS (экспериментально, класс KatanaMacOS)
Тот же протокол поверх IOKit напрямую (ctypes, без сторонних библиотек):
IOHIDDeviceSetReport / IOHIDDeviceGetReport с kIOHIDReportTypeFeature —
семантика 1:1 с hidraw, feature-репорты rid=0, 64 байта данных.
Устройство ищется через IOServiceGetMatchingServices("IOHIDDevice") + IOHIDDeviceGetProperty:
выбирается клавиатура 0C45:8006 с PrimaryUsagePage=1, PrimaryUsage=6 (boot keyboard = интерфейс 0).
Выбор транспорта автоматический по sys.platform (open_katana()): darwin → IOKit, остальное → hidraw.
Грабли macOS (все проверены на железе):
- у IOKit своя нумерация типов репортов: Input=0, Output=1, Feature=2;
с Output прошивка отвечает мусором (1 байт
04); IOServiceMatchingвозвращает CFDictionaryRef — без явногоrestype = c_void_pctypes обрезает указатель и процесс падает segfault'ом;IOHIDDeviceGetPropertyпринимает IOHIDDeviceRef (отIOHIDDeviceCreate), а не registry entry — с entry процесс падает segfault'ом; плоскихidVendorв свойствах реестра нет — только через GetProperty;- доступ к HID-клавиатурам выдаётся вручную: Системные настройки → Конфиденциальность и безопасность → «Input Monitoring» для приложения-терминала, затем перезапуск терминала;
- Karabiner-Elements (и подобные утилиты) захватывает клавиатуры монопольно — пока он работает,
IOHIDDeviceOpenвозвращаетkIOReturnExclusiveAccess(0xE00002C5); проверено диагностикойmisc/iokit_probe.py; reset(pyusb) на macOS работает (проверено: USB port reset проходит);- udev-правило не нужно.
Проверка на железе (macOS, Karabiner выключен): mode/paint/keys/remap/macro/reset — работают.
Общая транзакция (все команды)
SET 04 18 begin ACK: 04 18 00 01 (или ff = сессия уже открыта, ок)
SET 04 13 00*6 01 data-begin ACK: 04 13 00 01
SET <payload 64B> данные (без ACK)
SET 04 02 commit ACK: 04 02 00 01 <LE16 sum(payload)>
SET 04 f0 save (flash) ACK: 04 f0 00 01
- ACK-байт [3]:
01= ok,00/ff= отказ (залипшая сессия / неверное состояние). - Контрольная сумма:
sum(payload) & 0xFFFF, LE, в ACK коммита (байты 4–5). Проверена на десятках команд вендора и наших записях. - Сессию всегда закрывать
f0, иначе прошивка клинится: begin отвечает00, команды отклоняются. Лечение:katana.py reset(USB port reset, ~2–12 c на переподключение; иногда нужен повторный reset после паузы). - Вендор всегда шлёт полную транзакцию begin → data → payload → commit → save, даже на каждый чих. Подражать.
Lighting payload (блок 1)
[0] режим: 1..19 (0x13) — список вендора; 0x80 — custom per-key таблица
[1..3] R G B
[8] flag: 0x00 для режимов 1 и 4, 0x01 для остальных
[9] яркость 0x01..0x0F (вендор: слайдер 15 позиций)
[10] скорость 0x02..0x0F (вендор); для статики (mode 1) всегда 0x0A,
слайдер скорости в UI неактивен — подтверждено katana2-speed
[11] подрежим: моно/радуга для статики, направление анимации (см. ниже)
[14..15] aa 55
Байт [11] — подрежим (снято по color-toggle-*.pcapng)
| Режим | Значение | Смысл |
|---|---|---|
| 1 static | 00 / 01 |
моно (--color) / радужный спектр |
| 10 waterfall | 02 / 03 |
север→юг / юг→север |
| 16 marquee (0x10) | 01 / 00 |
восток→запад / запад→восток |
Для остальных режимов вендор всегда шлёт 00.
Направления режимов 11 flow, 12 rotate, 18 wind кодируются тем же байтом (значения не сняты — в захватах переключали только waterfall и marquee).
CLI: --rainbow, --direction north-south|south-north|east-west|west-east.
- Off = payload из нулей +
aa55(последний пункт списка вPендора). - Яркость масштабирует per-key цвета: 15/15 →
ee(238), 1/15 →0f. - Режим 0x80 отображает таблицу per-key (см. ниже) — это и есть «кастом».
Таблица режимов (описания вендорской утилиты, docs/color-modes.md)
Номер = байт [0] payload.
«моно/спектр» — режим рисует одиночный цвет или полный спектр независимо от него.
У режимов 10, 11, 12, 16, 18 есть направление анимации (кодировка в payload пока не снята — ждём снимок установки настроек).
Алиасы реализованы в katana.py (MODES).
| № | Алиас | Описание | Параметры |
|---|---|---|---|
| 1 | static |
Постоянный свет всех клавиш | моно/спектр, яркость |
| 2 | keypress |
Подсветка нажатых: всё выключено, горят нажатые | моно/спектр, яркость, скорость |
| 3 | fade |
Затухание нажатых: всё включено, гаснут нажатые | моно/спектр, яркость, скорость |
| 4 | star |
Звёздный: случайные вспышки после полного затухания | моно/спектр, яркость, скорость |
| 5 | snow |
Снежный: как звёздный, но больше соседних клавиш | моно/спектр, яркость, скорость |
| 6 | flower |
Цветочный: плавные цвета на каждой клавише без затухания | спектр, яркость, скорость |
| 7 | breathing |
Дыхание: плавное свечение и затухание всех клавиш | моно/спектр, яркость, скорость |
| 8 | spectrum |
Полный спектр: плавные цвета на всех клавишах | спектр, яркость, скорость |
| 9 | ripple |
Круги: плавные цвета кругами из центра к краям | моно/спектр, яркость, скорость |
| 10 | waterfall |
Водопад: спектр по рядам клавиш | моно/спектр, яркость, скорость, ↑/↓ |
| 11 | flow |
Течение: косые волны в одну сторону без затухания | моно/спектр, яркость, скорость, ←/→ |
| 12 | rotate |
Вращение: волна по кругу вокруг центра | моно/спектр, яркость, скорость, ←/→ |
| 13 | h-edge |
Горизонтальная грань: волна по ряду от нажатой | моно/спектр, яркость, скорость |
| 14 | v-edge |
Вертикальная грань: косая волна от нажатой клавиши | моно/спектр, яркость, скорость |
| 15 | splash |
Рябь по воде: плавные круги от нажатой клавиши | моно/спектр, яркость, скорость |
| 16 | marquee |
Бегущая строка: подсветка каждой клавиши в ряду | моно/спектр, яркость, скорость, ←/→ |
| 17 | mountains |
Горы: плавные углы из середины в стороны | моно/спектр, яркость, скорость |
| 18 | wind |
Ветер: косые волны в одну сторону с затуханием | моно/спектр, яркость, скорость, ←/→ |
| 19 | shuttle |
Шаттл: бегущие строки по чётным/нечётным рядам в обе стороны | моно/спектр, яркость, скорость |
| — | off |
Выключена (пункт 20 списка) | all-zero payload |
Переключение «моно/спектр» — байт [11] (для статики 00=моно, 01=радуга), направление анимации — тоже байт [11], значения в таблице выше.
Per-key таблица (кастомная раскраска)
Запись (реализовано, работает стабильно):
begin
→ SET 04 23 00*6 09
→ ACK
→ 9 пакетов по 64Б: 143 записи [index, R, G, B] (индексы 0x00..0x8e) + 0000 + aa55 (576 Б)
→ commit (ACK сумма = sum(576Б))
→ save
Затем lighting payload mode=0x80 показывает таблицу.
Чтение (04 f5) — РАБОТАЕТ (вендорская схема):
SET 04 f5 +09
→ GET ×9: поток [idx,r,g,b]×16, индексы 0x00,0x10..0x80 (0x00..0x8f)
→ commit (ACK 01, сумма 0000 — payload не писался)
→ save
- НЕ делать begin+data-begin перед f5 — data-begin disarmит поток (ACK status 00, GET-ы возвращают эхо последнего SET).
- Первый GET после SET может быть ACK
04 f5 00 01(наблюдалось на первом чтении после save у вендора, там же было только 8 пакетов) — читатель пропускает не-данные и ждёт пакеты с ожидаемыми индексами. - Указатель сбрасывается на 0x00 каждым f5 SET (в рабочем состоянии).
- Ретрай: до 3 попыток, между ними
begin(переоткрытие контекста). - Кадр = ЖИВОЕ изображение: на статике — цвет режима (масштаб яркости, 15/15 → ee), на анимации — текущие цвета эффектов, меняются между чтениями. Слот 0x6d всегда 000000 (null-слот прошивки, даже у вендора). Слепые слоты в кадре на статике показывают цвет режима (буфер расчётный, физически LED нет).
Переназначение клавиш (канал 04 11)
Запись — РАБОТАЕТ (снято по raw-data/katana-v1/keybind-mouse-buttons.pcapng: LCtrl → 5 кнопок мыши по очереди + возврат на default; проверено на железе):
begin
→ SET 04 11 00*6 09
→ ACK
→ 9 пакетов по 64Б: 143 записи по 4 байта (позиция слота = LED-индекс клавиши) + 0000 + aa55 (576 Б)
→ commit (ACK сумма = sum(576Б))
→ save
Формат блоба совпадает с per-key таблицей, но содержимое записи — назначенное действие, а не цвет. В отличие от per-key записи, индекс клавиши в записи НЕ дублируется — слот определяется позицией в блобе.
Содержимое записи (4 байта):
[00, 00, 00, 00]— действие по умолчанию (клавиша работает как обычно).[01, 01, код, 00]— кнопка мыши, код в байте [2].[02, маска, код, 00]— шорткат: маска модификаторов в байте [1], HID usage ID клавиши (Keyboard/Keypad page 0x07) в байте [2] (дампы keybind-editor, keybind-hotkeys).[03, usage_lo, usage_hi, 00]— мультимедиа/веб-действие, байты [1..2] — 16-битный LE usage ID из HID Consumer Page (0x0C) (дамп keybind-multimedia).[06, индекс, режим, счётчик]— запуск макроса (дампы macros-set, macros-modes). Индекс 0-based: «Макрос 1» вендорской утилиты = 0. Режим:00— однократно,01— повторитьсчётчикраз (наблюдалось 1, 5, 8),02— повторять до повторного нажатия.
Коды кнопок мыши (байт [2]; значения — степени двойки, вероятно битовая маска, но комбинированные записи железом не проверялись):
| Код | Кнопка |
|---|---|
01 |
ЛКМ (левая) |
02 |
ПКМ (правая) |
04 |
СКМ (средняя) |
08 |
«назад» (боковая) |
10 |
«вперёд» (боковая) |
Пример из дампа: LCtrl (слот 0x5b) → ЛКМ = запись [01, 01, 01, 00] по смещению 91×4 = 364 (пакет 5, смещение 44).
Шорткаты редактора (дамп keybind-editor): 9 транзакций, каждая вешает на Caps (слот 0x37) запись [02, 01, код, 00].
Код в байте [2] — HID usage ID буквы, порядок транзакций совпадает с порядком списка функций в UI вендора.
Реализация — EDITOR_ACTIONS в katana.py, офлайн-тест по дампу — tests/test_macro.py (test_editor_actions_match_dump).
Проверено на железе: caps=copy и menu=undo работают как Ctrl+C/Ctrl+Z.
| Код | Функция | Шорткат |
|---|---|---|
12 |
Открыть | Ctrl+O |
11 |
Создать | Ctrl+N |
1d |
Отмена | Ctrl+Z |
16 |
Сохранить | Ctrl+S |
06 |
Копировать | Ctrl+C |
1b |
Вырезать | Ctrl+X |
19 |
Вставить | Ctrl+V |
09 |
Найти | Ctrl+F |
04 |
Выбрать всё | Ctrl+A |
Горячие клавиши (дамп keybind-hotkeys): 10 транзакций, каждая вешает на Caps (слот 0x37) запись — по порядку списка UI вендора:
A, Shift+B, Ctrl+C, Alt+D, Meta+E, Esc, F1, Num1, Fn, «Мой компьютер».
Первые девять — записи типа 02, десятая — мультимедиа [03, 94 01, 00] (usage 0x0194 AL My Computer, см. таблицу выше).
Реализация — HOTKEY_MODIFIERS, HOTKEY_KEYS и parse_hotkey() в katana.py, офлайн-тесты — tests/test_macro.py (test_hotkey_actions_match_dump, test_parse_hotkey).
Проверено на железе: caps=ctrl+c (одиночный модификатор) и caps=ctrl+shift+c (маска 0x03 — комбинация) работают как ожидается.
Байт [1] типа 02 — битовая маска модификаторов:
| Маска | Модификатор |
|---|---|
00 |
без модификатора |
01 |
Ctrl |
02 |
Shift |
04 |
Alt |
08 |
Meta (Win) |
У шорткатов редактора наблюдался только 01 (Ctrl); дамп keybind-hotkeys подтверждает остальные значения и показывает, что маска — степень двойки.
Комбинации модификаторов (сумма масок, например Ctrl+Shift = 03) подтверждены на железе: caps=ctrl+c и caps=ctrl+shift+c работают.
Коды клавиш в байте [2] сверены с HID Usage Tables и дампом: A–E = 04–08, Esc = 29, F1 = 3A, Num1 = 59.
Fn = AF — собственный код прошивки: в HID Usage Tables такого usage нет, но вендор шлёт именно его.
| Код | Клавиша | Примечание |
|---|---|---|
04 |
A | (буквы 04–1D) |
29 |
Esc | |
3A |
F1 | (F-клавиши 3A–45) |
59 |
Num1 | (нумпад 58–62, Num0 = 62) |
AF |
Fn | собственный код прошивки, не HID |
Мультимедиа/веб-действия (дамп keybind-multimedia): 18 транзакций, каждая вешает на Caps (слот 0x37) запись [03, usage u16 LE, 00].
Порядок транзакций совпадает с порядком списка функций в UI вендора, все usages сверены с HID Usage Tables (Consumer Page 0x0C).
Реализация — MULTIMEDIA_ACTIONS в katana.py, офлайн-тест по дампу — tests/test_macro.py (test_multimedia_actions_match_dump).
| Usage | Функция |
|---|---|
0183 |
Плеер (AL Consumer Control Configuration) |
00CD |
Воспроизведение/пауза |
00B7 |
Стоп |
00B6 |
Предыдущая песня |
00B5 |
Следующая песня |
00E9 |
Громкость + |
00EA |
Громкость − |
00E2 |
Отключить звук |
0223 |
Домашняя страница (AL Internet Browser) |
0227 |
Веб: обновить (AL Web Refresh) |
0226 |
Веб: остановить (AL Web Stop) |
0224 |
Веб: назад (AL Web Back) |
0225 |
Веб: вперед (AL Web Forward) |
022A |
Веб: избранное (AL Favorites) |
0221 |
Веб: поиск (AL Web Search) |
0194 |
Мой компьютер (AL My Computer) |
0192 |
Калькулятор (AL Calculator) |
018A |
Электронная почта (AL Email Reader) |
Известные ограничения:
- Команда перезаписывает ВСЮ таблицу (вендорская утилита делает так же) — каждый раз шлются все 143 слота.
- Команда чтения текущих переназначений неизвестна (в дампах только запись).
- Установлены пять типов действий: мышь (
[01, 01, ...]), шорткаты — редактор и горячие клавиши ([02, маска, ...]), мультимедиа ([03, usage, ...]) и макрос ([06, ...]). Остальные типы (Fn-слой) не исследованы.
Макросы (канал 04 19 + привязка через 04 11)
Снято по дампам raw-data/katana-v1/macros-*.pcapng и вендорскому XML (test.xml, macros-create-test.xml).
Содержимое макросов хранится в самой прошивке, а не на ПК:
в эксперименте макрос сыграл при отключённой вендорской утилите и другой раскладке («ФЫВА» — те же клавиши A, S, D, F).
Запись содержимого — канал 04 19
Транзакция отличается от обычной:
SET 04 19 begin ACK: 04 19 00 01
SET 04 15 00*6 <число пакетов> data-begin ACK: 04 15 00 01 (эхо числа пакетов в байте [8])
SET <данные, пакетов × 64Б> данные (без ACK)
SET 04 02 commit ACK: 04 02 00 01 <LE16 sum>
- Вендор после коммита не делает save (04 f0) — в дампе macros-create его нет.
- В байте [8] data-begin — число 64-байтовых пакетов (как у 04 23/04 11, где 09 = 576/64).
- Модель сохранения (проверена на железе): каждая область (bind-таблица, paint-таблица, режим, макросы) сохраняется во флеш только своим собственным save (04 f0). Чужой save чужую область ни сохраняет, ни затирает: remap-save не трогает макросы, macro-save не трогает bind. Практическое правило: область переживает переподключение только если после её записи был её собственный save. В apply каждая секция несёт свой save.
- Запись макросов (04 19) сбивает bind-таблицу и режим подсветки В RAM (во флеше они остаются). После записи макросов bind и режим нужно перепослать (apply делает это автоматически).
- Несколько макросов живут только при записи одним блобом: каждый вызов 04 19 с блобом, содержащим один слот, обнуляет прочие слоты. При обращении к ПУСТОМУ слоту прошивка делает fallback на слот 0 (проверено: macro1 при пустом слоте 1 исполнял содержимое macro0).
- Вендорская утилита после записи макросов других save-транзакций не делает — в одиночной операции этого достаточно.
Формат блоба (дамп macros-create, 14 пакетов = 896 байт):
- Заголовок 400 байт (0x190) — 100 u32-смещений: смещение данных макроса от начала блоба, 0 = слот пуст. В дампе: слот 0 → 0x190 (400), слот 1 → 0x1d8 (472).
- На каждый макрос: u32 (число событий × 2), u32 0, затем события по 8 байт:
[00 00][код][тип][задержка u16 LE][00][50]. - Хвост: нули до кратности 64 минус 2 байта +
aa55(как у per-key блоба). - Ёмкость по дампу — 896 байт: один макрос вмещает до 60 событий (при пустых остальных слотах).
Типы событий (байт [3]): B0 — клавиша нажата, 30 — отпущена, 90 — кнопка мыши нажата, 10 — отпущена.
Коды клавиш (байт [2]):
- Буквы, цифры, F1–F12, модификаторы — совпадают с HID usage ID (сверено: A=04, S=16, Q=14, LCtrl=E0, LAlt=E2, LWin=E3).
- Стрелки — собственные коды прошивки, не HID: Left=
5C, Right=5E(сверено); Up=5D, Down=5F— предположение по соседству, железом не проверены. - Медиа — тоже коды прошивки: VolUp=
05, VolDown=06(сверено по XML: VK 175→05, VK 174→06). - Коды кнопок мыши — те же, что в таблице переназначений (
01ЛКМ,02ПКМ,04СКМ).
Коллизия 05/06: это одновременно VolUp/VolDown и HID-коды букв B/C.
Как прошивка различает их (и различает ли) — не установлено; вендорская утилита шлёт 05/06 именно для громкости.
Задержки: в двух событиях вендорского XML стоит delay_time="0", а в блобе прошивки — 10.
Похоже на минимум вендора (2 наблюдения).
Семантика задержки (пауза до или после события) по дампу не определяется — значения просто переносятся как есть.
Привязка макроса к клавише
Обычная транзакция канала 04 11 (см. «Переназначение клавиш»), запись [06, индекс, режим, счётчик].
Вендор после привязки делает save (в отличие от записи содержимого).
Вендорский XML
Утилита экспортирует макрос в XML (по файлу на макрос, имя — в macroinfo): item type 2/3 — клавиша вниз/вверх (value = VK-код Windows), 4/5 — кнопка мыши вниз/вверх (value: 1 = ЛКМ, 2 = СКМ, 3 = ПКМ), delay_time — мс.
Реализация: parse_macro_xml() / macro_to_xml() в katana.py, офлайн-тесты — tests/test_macro.py
(блоб, собранный из вендорских XML, побайтово совпадает с дампом с точностью до двух задержек «0 → 10»).
Открытые вопросы:
- Чтение содержимого макросов из прошивки не найдено (в дампах только запись).
- Смысл u32 «число событий × 2» (возможно, счётчик полусобытий).
- Реальный лимит макросов: заголовок рассчитан на 100 слотов, но ёмкости данных хватает лишь на пару десятков событий суммарно.
- Несохранённое (без 04 f0) содержимое стирается следующим save другой транзакции (проверено) — а вот переживает ли оно переподключение USB, не установлено.
- Коллизия кодов
05/06(громкость против букв B/C). - Коды Up/Down стрелок (
5D/5F— предположение) и остальные медиа-клавиши.
Особенность режима 7 (breathing): цвет из payload игнорируется
Проверено на железе серией экспериментов:
- Payload режима 7 побайтово совпадает с вендорским (
07 ff 00 00 … 01 0f 0a … aa 55), но LED-кадр показывает зелёно-жёлто-красную гамму (R=0, G:B ≈ 5:1) при любом цвете в байтах [1..3]: красный, зелёный и синий дают одну и ту же картинку. - Цвет честно работает в статике (mode 1: кадр
(238,0,0)для красного) и в fade (mode 3: кадр красноватый). - Speed, flag и paint-таблица (0x80) на это не влияют; после
defaultповедение то же.
Вывод: у breathing цветовой цикл зашит в прошивку, байты цвета для него не используются (возможно, вендорская утилита показывает то же самое — в её дампе тот же payload). При выборе режима для моноцвета используйте static или fade.
Карта клавиш (index → физическая клавиша)
Методика: красим 4–8 индексов контрастными цветами, пользователь называет клавиши.
Полная карта — в keymap.py (источник данных), ниже сводка.
| Индекс | Клавиша |
|---|---|
0x00 |
(слепой) |
0x01 |
Esc |
0x02 |
F1 |
0x03 |
F2 |
0x04 |
F3 |
0x05 |
F4 |
0x06 |
F5 |
0x07 |
F6 |
0x08 |
F7 |
0x09 |
F8 |
0x0a |
F9 |
0x0b |
F10 |
0x0c |
F11 |
0x0d |
F12 |
0x0e–0x12 |
(слепые) |
0x13 |
~ (`) |
0x14 |
1 |
0x15 |
2 |
0x16 |
3 |
0x17 |
4 |
0x18 |
5 |
0x19 |
6 |
0x1a |
7 |
0x1b |
8 |
0x1c |
9 |
0x1d |
0 |
0x1e |
- (_) |
0x1f |
= (+) |
0x20 |
NumLock |
0x21 |
Num/ |
0x22 |
Num* |
0x23–0x24 |
(слепые) |
0x25 |
Tab |
0x26 |
Q |
0x27 |
W |
0x28 |
E |
0x29 |
R |
0x2a |
T |
0x2b |
Y |
0x2c |
U |
0x2d |
I |
0x2e |
O |
0x2f |
P |
0x30 |
[ ({) |
0x31 |
] (}) |
0x32 |
Num7 |
0x33 |
Num8 |
0x34 |
Num9 |
0x35–0x36 |
(слепые) |
0x37 |
Caps |
0x38 |
A |
0x39 |
S |
0x3a |
D |
0x3b |
F |
0x3c |
G |
0x3d |
H |
0x3e |
J |
0x3f |
K |
0x40 |
L |
0x41 |
; |
0x42 |
' |
0x43 |
\ |
0x44 |
Num4 |
0x45 |
Num5 |
0x46 |
Num6 |
0x47–0x48 |
(слепые) |
0x49 |
LShift |
0x4a |
Z |
0x4b |
X |
0x4c |
C |
0x4d |
V |
0x4e |
B |
0x4f |
N |
0x50 |
M |
0x51 |
, |
0x52 |
. |
0x53 |
/ |
0x54 |
RShift |
0x55 |
Enter |
0x56 |
Num1 |
0x57 |
Num2 |
0x58 |
Num3 |
0x59–0x5a |
(слепые) |
0x5b |
LCtrl |
0x5c |
LWin |
0x5d |
LAlt |
0x5e |
Space |
0x5f |
RAlt |
0x60 |
Fn |
0x61 |
Menu |
0x62 |
RCtrl |
0x63 |
ArrowLeft |
0x64 |
ArrowDown |
0x65 |
ArrowUp |
0x66 |
ArrowRight |
0x67 |
Backspace |
0x68 |
Num0 |
0x69 |
Num. (Del) |
0x6a |
NumEnter |
0x6b–0x6f |
(слепые) |
0x70 |
PrtSc |
0x71 |
ScrLk |
0x72 |
(слепой) |
0x73 |
Pause |
0x74 |
Insert |
0x75 |
Home |
0x76 |
PgUp |
0x77 |
Delete |
0x78 |
End |
0x79 |
PgDown |
0x7a |
Num- |
0x7b |
Num+ |
0x7c–0x8e |
(слепые) |
Итого 104 клавиши, все 143 слота проверены, все физические клавиши светятся при полной заливке (проверено: «горят ВСЕ клавиши»). Нумпад полный: NumLock, /, *, -, 7/8/9, 4/5/6, 1/2/3, +, 0, ., Enter.
Слепые слоты (39 шт, не соответствуют ни одному LED): 0x00, 0x0e–0x12, 0x23, 0x24, 0x35, 0x36, 0x47, 0x48, 0x59, 0x5a, 0x6b–0x6f, 0x72, 0x7c–0x8e. Хвост 0x80+ — резерв прошивки под большие раскладки/модели.
Аномалия 0x80: в раннем тесте светился синим сам по себе; в финальном тесте (жёлтый) не горел. Считать слепым, но помнить про аномалию при чтении f5 (в потоке чтения 0x80 показывал 0000ff).
Урок картирования: тёмные/холодные цвета (синий) на дальних клавишах пользователь может не заметить — Num- (0x7a) был пропущен в синем тесте, но найден жёлтым при перепроверке слепых слотов группами. При сомнениях перепроверять слепые слоты яркой заливкой группами.
Статус утилиты katana.py
mode N --color RRGGBB [--brightness 1..15] [--speed 2..15] [--flag]
Валидация диапазонов. Для mode 1 speed форсируется 0x0A с warning.
off
Payload нулей + aa55.
default
Вендорский payload 0x80.
raw HEX
Произвольный payload в транзакции.
scan --from --to
Перебор режимов (1..19), каждый с save.
reset
USB port reset (unwedge).
paint --all RRGGBB [--key IDX=RRGGBB ...] [--black] [--keep] ...
Запись таблицы (143 клавиши) + показ mode 0x80.
--wasd = четыре --key для W/A/S/D.
--numpad = 17 --key для всего нумпада.
--alpha = 26 --key для букв A–Z.
--punct = 9 --key для знаков [];',./\ и пробела.
--digits = 13 --key для ряда `1234567890-=.
--arrows = 4 --key для стрелок.
--row1..--row6 = --key для горизонтальных рядов полной клавиатуры, включая нумпад (104 клавиши; высокие клавиши нумпада отнесены к ряду начала; состав — ROW_KEYS в katana.py).
--keep берёт базой текущий живой кадр (чтение f5), остальные клавиши сохраняют цвета.
В --key индексы задаются только в hex (0x25), голые цифры — имена клавиш цифрового ряда.
Клавиши со спецсимволами принимаются символом ([, ~, ;, ...), синонимом (tilde, minus, lbracket, semicolon, backslash, numenter, ... — таблица KEY_SYMBOL_ALIASES в keymap.py) и каноническим именем из keymap.py.
keys
Чтение живого кадра (04 f5).
remap --key KEY=ACTION [--key ...] [--clear]
Таблица переназначений (канал 04 11).
ACTION: мышь (lmb/mouse1, rmb/mouse2, mmb/mouse3, back/mouseback, forward/mouseforward), шорткаты редактора (open/new/undo/save/copy/cut/paste/find/selectall — Ctrl+O/N/Z/S/C/X/V/F/A), мультимедиа (player, play, stop, prev, next, volup, voldown, mute, home, refresh, webstop, webback, webforward, favorites, websearch, mycomputer, calculator, email), горячие клавиши (MOD+КЛАВИША: ctrl/shift/alt/meta + a..z, 0..9, f1..f12, esc, tab, enter, space, caps, backspace, num0..num9, fn — например ctrl+c, shift+b, meta+e), default/none/off, макросы (macro<N> — однократно, macro<N>:K — K повторов, macro<N>-toggle — до останова).
--clear — вся таблица в default.
ВАЖНО: каждый вызов перезаписывает всю таблицу — не указанные --key сбрасываются в default; чтение текущих переназначений неизвестно.
macro set N [токены] [--xml FILE] [--save]
Записать содержимое макроса N (канал 04 19, без save — как вендор; --save шлёт 04 f0).
Токены: [+-]имя[@задержка_мс] (+ нажать, - отпустить, без знака — нажать и отпустить; имена — MACRO_KEYS/MOUSE_ACTIONS); перед токенами с - нужен --.
ВАЖНО: перезаписываются ВСЕ слоты макросов — не указанные стираются.
После записи привязать: remap --key KEY=macro<N>.
macro clear [--save]
Стереть ВСЕ слоты макросов.
macro show FILE / macro export FILE [токены] [--name]
Офлайн: показать события из вендорского XML / сгенерировать вендорский XML из токенов (без обращения к железу).
Инструменты анализа
analyze_pcap.py <capture.pcapng> [out.txt]— USBPcap → лог SET/GET control-трансферов (merged setup+data для OUT; GET по irpid).raw-data/*.txt— распарсенные логи всех 5 захватов (katana1 + 4×katana2).- Захваты:
katana1.pcapng(базовый реверс),katana2-color-static(цвета),katana2-bright-static(яркость),katana2-speed(скорость),katana2-modes(все режимы + off). В режимах был случайный дубликат клика (mode 8 дважды). - Сессии записи таблицы клавиш в katana1: события #11 и #36 (0x23, блок 09).
Следующие шаги
Докартировать карту— ЗАВЕРШЕНО (104 клавиши, keymap.py).Починить чтение f5— ЗАВЕРШЕНО (вендорская схема: SET f5 → 9 GET → commit → save; без begin/data-begin; ретраи с валидацией индексов).- Именованные пресеты (WASD, дыхание и т.п.) поверх paint — keymap.py готов.
Переназначение клавиш: канал 04 20— УТОЧНЕНО: канал04 11(не 04 20), формат и мышиные действия сняты и реализованы (см. «Переназначение клавиш»). Шорткаты редактора (тип02) тоже сняты и реализованы (дамп keybind-editor). Горячие клавиши (тип02, маска модификаторов в байте [1]) сняты по дампу keybind-hotkeys и реализованы (HOTKEY_KEYS/HOTKEY_MODIFIERS,remap --key KEY=MOD+KEY). Осталось: другие типы действий (Fn-слой) и чтение таблицы.Демо-скрипты (радуга, волна) поверх per-key API + живое чтение f5— ЗАВЕРШЕНО, методика описана и проверена (extending.md): таблица обновляется живьём в режиме 0x80, ~1 кадр/с.Макросы: запись содержимого и привязка— ЗАВЕРШЕНО (канал 04 19 + записи[06, ...]в 04 11, командаmacro, см. «Макросы»). Осталось: чтение содержимого, коллизия кодов 05/06, стрелки Up/Down, остальные медиа-коды.
Важные грабли (не наступать повторно)
- pyusb + detach_kernel_driver = клавиатура перестаёт печатать. Только hidraw.
- Сессия без f0 = клин прошивки. Всегда save.
- После клина: reset может не помочь с первого раза — повторить с паузой 5–10 c.
- f5-чтение без begin+data-begin = мусор (указатель не с начала).
- begin ACK ff = «сессия уже открыта» — это ок, продолжать.
- Слепые слоты молча игнорируются прошивкой (ACK ok, но не горят).
- Пользователь различает цвета приблизительно: «розовый»=маджента, «жёлтый»=жёлтый/олива, «зелёный»=зелёный/тёмнозелёный. Для картирования использовать максимально контрастные цвета и мало точек за раз.