# GANSS ARDOR_Katana (0c45:8006) — протокол и состояние реверса > Файл содержит ВСЁ необходимое для продолжения работы: протокол, карту клавиш, открытые вопросы, методику. > Обновлять по мере находок. > Пользовательская документация (установка, команды, режимы, свои скрипты) — по ссылкам из [README.md](../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`) + modalias `hid:b0003g0001v00000C45p00008006`. ## Транспорт (реализовано в katana.py) Feature-репорты через hidraw ioctl (`HIDIOCSFEATURE`/`HIDIOCGFEATURE`), буфер 65 байт: первый байт = report id (0). **usbhid не отцепляется** — печать никогда не прерывается. pyusb-вариант НЕ использовать (отцеплял драйвер). ## Общая транзакция (все команды) ``` SET 04 18 begin ACK: 04 18 00 01 (или ff = сессия уже открыта, ок) SET 04 13 00*6 01 data-begin ACK: 04 13 00 01 SET данные (без ACK) SET 04 02 commit ACK: 04 02 00 01 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 ``` - Вендор после коммита **не делает 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` — однократно, `macro:K` — K повторов, `macro-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`. ### `macro clear [--save]` Стереть ВСЕ слоты макросов. ### `macro show FILE` / `macro export FILE [токены] [--name]` Офлайн: показать события из вендорского XML / сгенерировать вендорский XML из токенов (без обращения к железу). ## Инструменты анализа - `analyze_pcap.py [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). ## Следующие шаги 1. ~~Докартировать карту~~ — ЗАВЕРШЕНО (104 клавиши, keymap.py). 2. ~~Починить чтение f5~~ — ЗАВЕРШЕНО (вендорская схема: SET f5 → 9 GET → commit → save; без begin/data-begin; ретраи с валидацией индексов). 3. Именованные пресеты (WASD, дыхание и т.п.) поверх paint — keymap.py готов. 4. ~~Переназначение клавиш: канал 04 20~~ — УТОЧНЕНО: канал `04 11` (не 04 20), формат и мышиные действия сняты и реализованы (см. «Переназначение клавиш»). Шорткаты редактора (тип `02`) тоже сняты и реализованы (дамп keybind-editor). Горячие клавиши (тип `02`, маска модификаторов в байте [1]) сняты по дампу keybind-hotkeys и реализованы (`HOTKEY_KEYS`/`HOTKEY_MODIFIERS`, `remap --key KEY=MOD+KEY`). Осталось: другие типы действий (Fn-слой) и чтение таблицы. 5. ~~Демо-скрипты (радуга, волна) поверх per-key API + живое чтение f5~~ — ЗАВЕРШЕНО, методика описана и проверена ([EXTENDING.md](EXTENDING.md)): таблица обновляется живьём в режиме 0x80, ~1 кадр/с. 6. ~~Макросы: запись содержимого и привязка~~ — ЗАВЕРШЕНО (канал 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, но не горят). - Пользователь различает цвета приблизительно: «розовый»=маджента, «жёлтый»=жёлтый/олива, «зелёный»=зелёный/тёмнозелёный. Для картирования использовать максимально контрастные цвета и мало точек за раз.