Кто как, а я обожаю странные маленькие девайсы. Так я однажды захотел себе HID Remapper (оригинальный или собственный, не суть). Это такие устройства, которые позволяют вам митмить HID-репорты (от клавиатуры, мышки, руля, геймпада и т.д.), придавая им новые смыслы. Например, можно сделать любую клавиатуру чуть более умной (с поддержкой слоёв, макросов и т.д.). В общем, прикольная штука.
А после экспериментов с “тисканьем” юбикеев (см. H4ptiX: услада для ваших юбикеев) я решил, что страшно хочу HID Remapper с возможностью “тискать” YubiKey. Так и родился проект Twibby, о котором и пойдет речь в этом посте.
Дела железные
Несмотря на то, что у проекта HID Remapper есть целая линейка спроектированных бордов на любой вкус и цвет (см. Remapper: Custom boards), мне ни один из них не подошёл. Причина очевидна из описания поста — нужно было куда-то воткнуть YubiKey :)
Поэтому я решил пойти по своим собственным граблям и собрать HID Remapper с USB-хабом на борту.
В плане компонент я был не очень оригинален:
- роль основного контроллера взял на себя RP2040 Core A. Можно было бы распаять RP2040 самому, но это не укладывалось в концепт (дальше поймете)
- FE1.1S revB в роли контроллера USB-хаба. Тут выбор чипов довольно большой, и, наверное, самый популярный среди “USB2.0 хаб за $1” — это SL2.1A (стоит дешево, минимальный BOM, работает), но я обещал себе не брать IC без документации, а потому выбрал FE1.1S :)
- BFR182 (прям как в H4ptiX) для замыкания контактной площадки YubiKey на землю по сигналу от контроллера.
- пара Type-C коннекторов (upstream/downstream) и один Type-A для YubiKey.
- сопутствующая SMD-рассыпуха.
С компонентами определились, время рисовать схемку:
Изначально у меня была [странная] идея сделать Twibby
чуть более универсальным и дать возможность как митмить репорты, так и снифать их. Потому резисторы R1 и R2 выполняют роль джамперов и подключают/отключают HID downstream
порт от хаба. Идея, конечно, так себе, но перезаказывать платы уже не хотелось, так что оставил как есть. Если будете повторять — не наступайте на мою граблю и выпилите эту часть логики совсем :)
Со схемой закончили, время развести платку:
Я понимаю, не произведение искусства, но я только учусь. С самого начала я целился в двустороннюю 4-слойную плату размером до 50×50 мм. 4 слоя — чтобы меньше страдать, а 50×50 мм — чтобы уложиться в промо от JLCPCB, где производство 4-слойной платы такого размера стоит $2 (+$1.5 доставка в Таиланд).
Для желающих поштырить поближе, я залил всё в онлайн-редактор.
С платой определились, время делать заказик на JLCPCB:
Итого: $25 за 5 плат вместе с доставкой. Из них $21 — это монтаж одной стороны платы (с учетом стоимости компонент), на которой расположены USB-хаб и прочая мелкая SMD-рассыпуха. Таков был план: одну сторону монтирует JLCPCB, вторую (более простую) — я сам. План сработал, я доволен :) Конечно, можно было бы чуть сэкономить и запаять хотя бы type-C разъёмы самому, но я чёт поленился.
Немного ожидания — и вот будущий HID Remapper ждёт допайки:
Немного запаха припоя — и наш HID Remapper собран:
Дела софтовые
Сначала я думал просто отснифать USB-пакетики (и потому закладывал поддержку режима сниффера на уровне железа), но потом так и не придумал, как корректно отличать репорты хоста от репортов подключённого устройства — и отбросил эту идею. Можно, конечно, напилить стейт-машину и считать флипы или опираться на тайминги, но звучит это не особо надёжно.
А вот с HID Remapper всё выглядит уже более безопасно: у него два USB PHY (один настоящий в RP2040, второй реализован на PIO с помощью Pico-PIO-USB), а значит, никакой путаницы. Но, несмотря на то что проект бесспорно клёвый, мне не понравились две вещи:
- Отсутствие прозрачного проксирования — вместо копирования оригинальных дескрипторов и митма репортов, он отправляет в хост собственный дескриптор. Почему сделано именно так — понятно, но это означает, что с точки зрения хоста подключено не оригинальное устройство, а сам HID Remapper.
- Отсутствие какой-либо авторизации (кроме файловых прав) для доступа к конфигурационному интерфейсу. Конфигурируется HID Remapper через WebHID (работает только в chromium-based браузерах), а значит, малваре на хосте ничего не стоит переписать фирмварь ремаппера по своему желанию и творить всякую дичь :/
И если первое для меня не критично, то второе — блокер, потому что ломает всю идею использования touch policy в YubiKey. Поэтому ничего не оставалось, кроме как форкнуть оригинальный проект и дописать авторизацию.
Долго думал, должна ли это быть отдельная кнопка на плате или клавиша клавиатуры. Решил, что с клавиатурой будет удобнее — так и сделал: добавил новый usage page
и позволил настраивать включение/выключение конфигурационного интерфейса.
Ну да хватит болтовни — закидываем twibby_rp2040.uf2 на флешку RP2040, подключаем к хосту и настраиваем (интерфейс для настройки тоже пришлось форкнуть: https://twbcfg.buglloc.com/):
маппинги (“тискаем” юбик клавишей
Pause
и включаем/выключаем конфигурационный интерфейс медиа-клавишейPlay/Pause
):и требование авторизации:
Пушим настройки на устройство и проверяем в деле:
То что доктор прописал :)
Итоги и мысли на будущее
Не могу сказать, что такой способ “тисканья” юбика радикально улучшает UX, но тянуться к Pause
явно ближе :)
Из явных промахов — надо было добавить серию диодиков (или RGB вроде WS2812) для индикации текущего слоя клавиатуры. Но знаете ли, я сильно быстрее умных мыслей! Может быть, в будущем сделаю новую ревизию или, как вариант, попробую заменить RP2040-Core-A на RP2350-Core-A, у которого как раз есть встроенный WS2812 на платке.
Но сейчас выглядит так, что нужно:
- ещё чуть пожить с этим. Пока шел второй день эксперимента и не все валенки собраны ^_^
- проверсти полноценный аудит фирмвари или написать свою
А уже потом решать нужна ли новая ревизия, мусорное ведро или третья смешная опция ;)
На этом у меня всё. Всем кота! (づ˶•༝•˶)づ♡