554 lines
43 KiB
Markdown
554 lines
43 KiB
Markdown
# 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 <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).
|
||
|
||
## Следующие шаги
|
||
|
||
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, но не горят).
|
||
- Пользователь различает цвета приблизительно: «розовый»=маджента, «жёлтый»=жёлтый/олива, «зелёный»=зелёный/тёмнозелёный.
|
||
Для картирования использовать максимально контрастные цвета и мало точек за раз.
|