Транспорт и транзакции¶
Обмен с прошивкой идёт HID feature-репортами.
Одна запись настроек всегда состоит из одной и той же последовательности команд, а каждая область прошивки сохраняется во флеш только своей командой 04 f0.
Linux: hidraw¶
Устройство открывается как /dev/hidrawN, обмен идёт через ioctl.
| Операция | Запрос | Буфер |
|---|---|---|
| Отправка | HIDIOCSFEATURE |
65 байт: [0] = report id 0 |
| Чтение | HIDIOCGFEATURE |
65 байт: [0] = report id 0 |
Особенности:
- драйвер
usbhidне отцепляется, поэтому клавиатура продолжает печатать во время настройки; - между запросами выдерживается пауза
0,04с — вендорский тайминг; - root не нужен, если установлено udev-правило
70-ganss-katana.rules; pyusbсdetach_kernel_driverиспользовать нельзя: после этого клавиатура перестаёт печатать.
macOS: IOKit¶
Поддержка экспериментальная.
Тот же протокол работает поверх IOKit напрямую через ctypes, сторонние HID-библиотеки не обязательны.
| Операция | Функция IOKit | Тип репорта |
|---|---|---|
| Отправка | IOHIDDeviceSetReport |
kIOHIDReportTypeFeature |
| Чтение | IOHIDDeviceGetReport |
kIOHIDReportTypeFeature |
У IOKit своя нумерация типов: Input = 0, Output = 1, Feature = 2.
С типом Output прошивка отвечает мусором: приходит один байт 04.
Поиск сервиса идёт по трём ступеням:
- через
hidapi(пакетhid), если он установлен: библиотека различает интерфейсы и отдаёт registry entry ID в пути устройства (DevSrvsID:…); - перебором
IOHIDDeviceс фильтром поPrimaryUsagePage=1иPrimaryUsage=6(boot keyboard); - первым интерфейсом с нужными VID и PID, если boot keyboard не найден: не тот интерфейс просто не ответит на ACK.
Если устройство не видно ни через hidapi, ни через IOKit, выводится сообщение «not found on USB».
Грабли macOS:
IOServiceMatchingвозвращаетCFDictionaryRef, поэтому безrestype = c_void_pctypes обрезает указатель и процесс падает;IOHIDDeviceGetPropertyпринимаетIOHIDDeviceRef, а не registry entry; плоскихidVendorв свойствах реестра нет;- доступ к HID-клавиатурам выдаётся вручную в «Input Monitoring», после чего терминал нужно перезапустить;
- Karabiner-Elements и подобные утилиты захватывают клавиатуру монопольно, и
IOHIDDeviceOpenвозвращаетkIOReturnExclusiveAccess(0xE00002C5); - udev-правило на macOS не нужно;
resetчерезpyusbна macOS работает, хотя пакеты собираются только под Linux.
Проверено на железе: при выключенном Karabiner команды mode, paint, keys, remap, macro и reset работают.
Общая транзакция¶
SET 04 18 begin ACK: 04 18 00 01
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
Разбор шагов:
beginоткрывает сессию; ответffвместо01означает, что сессия уже открыта, и это не ошибка;data-beginобъявляет начало блока данных, байт[8]задаёт параметр блока: у таблиц раскраски и переназначений это09, у макросов — число пакетов;- данные отправляются пакетами по 64 байта без подтверждения;
commitфиксирует блок, а в байтах 4–5 ACK возвращается контрольная сумма;saveзаписывает область во флеш.
ACK и контрольная сумма¶
| Поле | Смысл |
|---|---|
ack[0..1] |
эхо команды, например 04 02 |
ack[3] |
статус: 01 — успех, 00 или ff — отказ |
ack[4..5] |
sum(payload) & 0xFFFF, little-endian, только у commit |
Контрольная сумма проверена на десятках команд вендорской утилиты и на записях проекта. Если сумма не совпала, утилита печатает предупреждение, но продолжает работу.
Сохранение областей¶
У каждой области прошивки свой save.
Чужой save чужую область не сохраняет и не затирает.
| Область | Команда записи | Свой save |
|---|---|---|
| Режим подсветки | 04 18 |
04 f0 |
| Раскраска | 04 23 |
04 f0 |
| Переназначения | 04 11 |
04 f0 |
| Макросы | 04 19 |
04 f0 |
Практическое правило: область переживает переподключение только тогда, когда после её записи был её собственный save.
Вендорская утилита после записи макросов save не делает, но в проекте apply сохраняет каждую секцию.
Открытие сессии и клин¶
Сессию нужно закрывать командой 04 f0.
Если этого не сделать, прошивка залипает: begin начинает отвечать 00, а команды отклоняются.
Лечение:
- повторить команду;
- выполнить
katana.py reset— USB port reset без переподключения, обычно занимает 2–12 секунд; - если reset не помог с первого раза, повторить его с паузой 5–10 секунд;
- в крайнем случае выключить и включить клавиатуру физически.
Тайминги¶
| Операция | Время |
|---|---|
write_key_table(save=False) |
~0,6 с |
write_key_table(save=True) |
~0,7 с |
read_frame() |
~0,6 с |
lighting() (смена режима) |
~0,4 с |
| Чтение кадра без save | ~15 кадров/с |
Паузу 0,04 с сокращать не стоит: прошивка начинает терять пакеты и подтверждения.
Чтение живого кадра идёт по отдельной схеме с паузой 5 мс — о ней рассказано в разделе Раскраска клавиш.