NRKO на USB. Проблемы и костыли при их решениях

Привет, GT. В одном из моих прошлых постов (в частности, про клавиатуру SteelSeries Apex M800) юзернейм monah_tuk задал интересный вопрос в комментариях: как реализуется NKRO (n-key rollover, одновременное зажатие нескольких клавиш) в USB-клавиатурах. Данный вопрос меня заинтересовал, а когда я копнул поглубже… В общем, там такие авгиевы конюшни, что на целый пост информации хватило, пусть и небольшой.

880ae353bc054764b5b00e78560f2c91.jpg

Казалось бы, USB 2.0 даже в режиме Low-speed обеспечивает передачу до 1500 КБит в секунду (порядка 185 КБайт/с.), в чём проблема передать нажатия клавиш? Но не всё так просто.
Чтобы разобраться с тем, какие проблемы у USB-подключения, нам придётся покопаться в истории, и немного — в конструкции клавиатуры.

Проблемы rollover’а: ghosting


Допустим, у вас есть клавиатура со стандартными 104/105 клавишами. В идеальном случае, чтобы получить 100% точную информацию о том, какие клавиши зажаты, вам понадобится на 1 «проводок» больше, чем есть кнопок: 104/105 проводов от кнопок, и одна общая «земля».

Разумеется, такая разводка будет сложна и дорога, поэтому многие недорогие клавиатуры применяют внутри т.н. «матрицу» — набор пересекающихся горизонтальных и вертикальных контактов. Контроллер сканирует каждый столбец, обнаруживает сигнал, начинает сканировать «строки» и вычисляет нажатую клавишу.

8a9db09a960c7e2381df3ba06addc245.png

В случае, если вы нажимаете, скажем, S и G контроллер уже не может сказать, какая из линий была замкнута: в лучшем случае обнаружит одну из двух, в худшем — из-за особенностей разводки нарисует какую-нибудь третью, которую вы не нажимали. Это называется ghosting.

Лечится подобное поведение различными хитростями в области разводки: наиболее популярные сочетания вешаются на разные «линии», функциональным клавишам, которые часто являются элементами хоткеев, назначаются отдельные линии. Разводка остаётся достаточно простой, но в недорогих клавиатурах редко встречается 5+KRO — обычно дело ограничивается 3-4 одновременно зажатыми клавишами.

Кроме того, контроллер может блокировать «соседей» по блоку, чтобы убрать «лишние» срабатывания. То есть формально клавиатура может поддерживать 6KRO, и честно давать нажать CTRL+AWFBNM одновременно, но не давать зажать AWSD, ограничиваясь срабатыванием вида WAS или WDS.

Проверить свою клавиатуру вы можете с помощью вот такой штуки. У неё есть некоторые ограничения, но они обусловлены тем, что система перехватит нажатие кнопки раньше, чем браузер. Например, Print Screen или Alt+Tab отработают раньше, чем страничка сможет их «поймать».

Сейчас эта ужасная (с точки зрения работы) «дешёвая» система почти полностью вытеснена немного более сложной, «открытой» матрицей, однако, экземпляры до сих пор встречаются, особенно часто во всяких «однодолларовых» комплектных клавиатурах, которые идут вдовесок к компьютеру «из коробки».

В более дорогих (зачастую, игровых или бизнес-клавиатурах) используются более сложные схемы, несколько контроллеров, индивидуальное подключение, многослойные печатные платы, в общем, различные подходы, которые уменьшают или полностью убирают ghosting, но приводят к росту цены устройства. С потенциальными аппаратными проблемами NKRO мы разобрались, переходим к софтвёрным.

Клавиатуры до USB


Основным распространённым разъёмом для подключения клавиатуры и мышки до USB был PS/2. Вот такой, кругленький:

f5b7a6e905986a83ea8ad3fac4e5b6b2.jpg

Его прелесть заключалась в том, что при соблюдении всех стандартов с одного разъёма и разветвителя можно было работать и с мышью, и с клавиатурой, а ещё сами устройства ввода генерировали прерывание и отправляли информацию о нажатых кнопках / перемещении курсора. Там хоть лицом по клавиатуре катайся, если всё разведено грамотно, и rollover не упирается в аппратные возможности клавиатуры — клавиатура отправит всё, что «прочитает» контроллер.

К сожалению, у PS/2 были и недостатки (вроде проблем с Plug’n’Play и неудобного разъёма), да и повсеместное распространение USB привело к тому, что PS/2 периферия была практически полностью вытеснена с рынка. Тем не менее, встречаются вполне современные материнки с USB 3.0 и двумя(!) PS/2:
110db6de3dee099c328da97f5ced88cb.png

Комбинированные порты, кстати, благодаря особенностям распиновки (общее питание, разные pin’ы для data+/data- у мыши и клавиатуры), позволяют также подключить оба устройства через один порт с помощью переходника:

dbdc5800929686000cda4c2b39a268ed.jpg

Проблемы NKRO на USB: во всём виноват USB HID


Для работы клавиатур через USB до загрузки системы (то есть в BIOS / UEFI) используется стандартный HID-драйвер, который был разработан для максимально широкой поддержки клавиатур и USB-хостов. Класс USB HID описывает устройства таким образом, чтобы стандартная реализация USB-хоста и USB-устройства могли взаимодействовать без установки специальных драйверов.

Обычная клвиатура (разработанная с поддержкой USB HID стандарта) использует выходной (исходящий от USB-хоста) поток данных (endpoint USB-стэка) для получения информации о состоянии индикаторов (Num / Caps / Scroll Lock), и входной поток (исходящий от клавиатуры, «входящий» для USB-хоста) для отправки информации о нажатых клавишах.

Если следовать наиболее строгой спецификации USB HID v1.11, которая поддеживает режим USB Boot (и позволяет использовать USB-клавиатуру для входа в BIOS и работы в нём), то клваиатура будет отправлять пррывания на CPU каждый раз, как USB-хост будет опрашивать её, вне зависимости от того, изменилось её состояние или нет. Таким образом «эмулируется» работа PS/2 клавиатур минимальными издержками.

В данном случае клавиатура работает по стандарту USB 1.1 (иногда 2.0) Low-speed, а частота опроса порта составляет 100 Гц. В данном режиме максимальная длина пакета на USB равна 8 байтам, а отправляются они раз в 10 мс. Один из байтов зарезервирован, так что на работу клавиатуры остаётся 7 байт, т.е. количество достаточное, чтобы закодировать нажатие любой клавиши-модификатора и ещё шести других.

Получится, кто клавиатура работает в режиме 6-KRO, и он является стандартным для большинства USB-клавиатур. Сделать меньше — можно (иногда осознанно, иногда — нет, из-за ghosting’а и key jamming’а), больше — только с нарушением спецификации USB HID.

Уже после загрузки система может увеличить частоту опроса USB-порта, загрузить специальный драйвер, который позволит работать, скажем, мультимедийным клавишам и макросам.

Реализации NKRO на USB


Обходы ограничений USB HID, собственно, встречаются двух видов. В первом случае клавиатура обнаруживается фирменным ПО, переводится в какой-нибудь специальный режим и отправляет данные с контроллера напрямую в драйвер, а тот уже общается с системой. Минус такого решения — увеличивается т.н. input lag: задержка ввода. Опросив знакомых, нашёл только одну клавиатуру, которая позволила нажать 10 клавиш при подключении по USB: махровый китайский ноунейм с закосом под «игровую». Владелец живёт аж в Иркутстке, так что не удивительно, что только у него нашлась такая штука.

Второе решение подкупает своей простотой и бесхитростностью, но по сути является костылём:

244ecd20d056e4d0475d82b8a9a9bc68.png
За скриншот спасибо MyFearGear, такую штуку заметили за Logitech G710+

Клавиатура просто представляется системе несколькими HID-устройствами, каждое из которых имеет свои 6KRO. Беглый опрос знакомых с просьбой указать модель клавиатуры и прислать такой скриншот показал, что этим «хаком» пользуются и Logitech, и A4tech, и Cougar, и SteelSeries — в общем все, у кого нашлось NKRO в описании клавиатур, подключащихся по USB.

Внимание! Прежде чем проверять свою клавиатуру, убедитесь, что у вас не подключена какая-нибудь навороченная многокнопочная USB-мышь (даже по беспроводному каналу). Так как всё многокнопочное великолепие точно так же может притворяться «лишними» клавиатурами.

Заключение


Собственно, на этом всё. Почти все производители выбрали «костыльный», но работающий и не создающих пользователю особых проблем метод, и честный (ну, сравнительно честный) NKRO на USB достижим.

© Geektimes