USB: новый интерфейс и проблемы со скоростью.

Вероятно, среди читателей нашего блога найдутся люди, которым в целях общего развития или из-за практических нужд будет интересно узнать: «Как организовать на USB-устройстве новый интерфейс?». На удивление, не так сложно. Сложнее заставить его работать как следует.

Про наши наработки в этом направлении сейчас и будет рассказано.

Приступить к созданию второго USB-интерфейса на нашей измерительной карте-анализаторе потока E1 для измерительного же прибора «Bercut MMT» b4.5 нас заставила, само собой, необходимость. Дело в том, что одна из функций b4.5 — это анализ протоколов сигнализации, таких как ОКС 7, EDSS. Сбором трейсов занимается карта, а их анализом — платформа b4.5. Анализ может проводиться и в реальном времени, а значит, перед нами встала задача организации интерфейса между картой и платформой. Этот интерфейс должен был позволять передавать в реальном времени данные со скоростью 120 кбайт/с. А физически карта и платформа могут общаться только посредством USB. И, на момент решения задачи, уже успешно общались! Так почему же новый интерфейс?

Дело в том, что:

  1. Интерфейс, который уже существовал, — текстовый. Он предназначен для обмена командами между картой и платформой, сбора результатов измерений и так далее. Но данные протоколов сигнализации изначально представлены в бинарном виде. Зачем их конвертировать в текстовый?
  2. Проблема скорости. Справедливости ради, 120 кбайт/с — не такая уж большая скорость, что-то около одного Мбит/с, однако существующий интерфейс USB Serial не давал и этого. USB, используемая на нашей карте, может работать в режиме full speed, то есть теоретический предел скорости для нее — 12 Мбит/с. Практическая же скорость — порядка 30-35 кбит/с. Разница — в 3 порядка, и этого совершенно недостаточно для решаемой задачи. К счастью, нет необходимости в двухстороннем обмене между платформой и картой, как подразумевается для USB serial. Нам необходим только поток от карты на b4.5.

Было решено все же организовать примитивный второй интерфейс и измерить скорость на нем.

Итак, как же создать интерфейс для USB? И что такое интерфейс в терминах USB? Хорошим подспорьем послужил вот этот ресурс.
Но здесь и сейчас в двух словах мы опишем основные моменты.

Основное понятие для USB — endpoint. Это окончание канала данных, которое может быть источником или приемником информации. Каждый endpoint работает только в одном направлении. Но для описания устройства одних endpoint’ов недостаточно.

Всякое USB-устройство описывается некоторой совокупностью дескрипторов. Эти дескрипторы образуют иерархию.
На самом высоком уровне иерархии — Device Descriptor. Здесь хранится информация о производителе устройства, серийном номере устройства и другая самая общая информация. Он у каждого устройства один.
Следующий — Config Descriptor. Здесь содержится в основном информация о режимах питания устройства. Их может быть несколько, но одновременно активный в каждый момент времени — один.
Config Descriptor’у соответствуют Interface Descriptor’ы — совокупность endpoint’ов, выполняющих определенную функцию. Вот почему речь и идет об организации еще одного интерфейса: обмен командами и сбор трейсов сигнализации — совершенно разные функции! Более понятный пример — МФУ принтер/сканер/копир, подключенное к PC по одному USB-интерфейсу. Каждой функции такого МФУ, очевидно, будет соответствовать свой Interface Descriptor. Понятно, что в пределах одной конфигурации Interface descriptor’ов может быть много, и нет ограничения на то, сколько из них будут активными.
И, наконец, каждый Interface Descriptor содержит в себе один или несколько Endpoint Descriptor’ов. Эти дескрипторы описывают, что за endpoint используется в составе данного интерфейса( то есть его номер ), каково его направление и режим работы.

Так вот, первое, что Вы должны сделать, чтобы организовать новый интерфейс — это создать соответствующие дескрипторы. Без них хост просто не узнает, какие возможности есть у вашего устройства.

Второе, что нужно, — собственно реализовать процедуры обмена по этому интерфейсу. В нашем случае это вылилось в дописывание драйвера для микроконтроллера карты. Это достаточно интересно, но здесь описываться не будет — все, в конечном итоге, упирается в изучение Datasheet’ов.

А третье — это драйвер на хосте. К счастью, тут на помощь пришла замечательная библиотека libusb. Она позволяет работать напрямую с endpoint’ами, которые в Linux отображаются в /dev/bus/usb/… К счастью, они там видны, если на устройстве есть нужный дескриптор. Кстати, при помощи libusb можно легко искать устройство по его изготовителю, серийному номеру, каким-то другим описанным в дескрипторах параметрам. Да и организация обмена тоже очень удобно реализуется.

В конечном итоге, тест все-таки был проведен. С нового endpoint’a безо всякой обвязки посылались двоичные данные на хост, и программа на хосте проверяла их целостность и измеряла скорость. Полученное значение приятно удивило — 500 кбайт/с. То есть не 12 Мбит/с, конечно, но на несколько порядков больше прошлого результата и вполне достаточно для передачи трейсов.
В последствии тестовая программа была доведена до ума, интегрирована в проект, и теперь второй интерфейс существует и работает.

В чем же была проблема? Источников несколько:

  1. USB Serial. Он очень тормозил передачу. К большому сожалению, на настоящий момент я не могу точно указать место в коде, которое приводило к такому замедлению обмена, но ясно одно — реализация USB Serial может быть улучшена, если понадобится.
  2. Способ вывода. Поскольку программа для измерения скорости была тестовой, перваой информацией, которая передавалась от карты к хосту, была фраза «Hello, world!» (думаю, никого это не удивит). Выводилась она в текстовом виде. И скорость обмена была порядка 375 кбайт/с. После того, как данные стали выдаваться в двоичном виде, она увеличилась до 500 кбайт/с. Весьма ощутимый прирост. Впрочем, это и неудивительно. Было ожидаемо, что вывод данных в текстовом виде создает большую нагрузку на процессор.
  3. Хостовая часть. После отладки на PC тест повторили с платформой b4.5, и скорость упала опять до 375 кбайт/с. ( на платформе Colibri PXA320, он послабее, чем почти 2-гигагерцовый Celeron ). Так что не во всем карта виновата.
  4. И, наконец, самое главное. Почему же не 12 Мбит/с. Дело в том, что USB-пакеты перед отправкой буферизируются. Максимальный размер пакета равен 64 байта, а буфер под хранения пакетов может вмещать в себе и 2, и 4, и 8, и так далее. До 16 килобайт. В самом простом случае буфер содержит в себе один пакет. При такой длине буфера упрощается процедура обмена по USB, но страдает скорость. И это как раз наш случай.

Что ж, для наших нужд скорости USB вполне хватает, а если нам потребуется больше — мы знаем, в каком направлении двигаться, и, если придется, заставим шину работать как следует. Но пока — есть к чему стремиться.