Уровни сигналов в usb
Собственно говоря, про то, как происходит передача данных мы уже начали говорить ещё в прошлой статье (помните, мы обсуждали конечные точки, коммуникационные каналы и прочее), просто здесь мы обсудим это более детально и обстоятельно.
А дальше с ними начинает работать USBD.
Здесь у нас всплыл термин «транзакции», поэтому поясню, что это такое. Транзакция — это один сеанс связи с устройством. Поскольку к шине у нас подключено много устройств, то хост физически не может постоянно и одновременно обмениваться данными со всеми этими устройствами. Вместо этого он организует циклы (фреймы, кадры) в каждом из которых осуществляет несколько сеансов связи с различными устройствами. Вот эти сеансы связи и называются транзакциями.
Кадры следуют друг за другом с периодичностью 1 кадр в мс. Ещё раз замечу, что в одном кадре не обязательно должны присутствовать сеансы связи со всеми устройствами на шине или сразу все кусочки информации, предназначенные для одного устройства. Расписание транзакций планируется USBD с учётом приоритета выбранных типов передач и с какими-то конечными точками хост может не осуществлять транзакций несколько фреймов подряд, даже при наличии запроса на обмен данными с этими точками (помните, мы в первой части обсуждали, что принтер может и подождать, а вот передача музыки в USB колонки ждать никак не может). Образно кадры и транзакции показаны на рисунке справа, подробнее их структуру мы рассмотрим позднее.
Вот теперь, с учётом новой информации, мы можем снова вернуться к типам передач и пропускной способности канала. Что для изохронных передач означает способность занять 90% пропускной способности канала. Это значит, что в каждом кадре 90% времени может быть отведено для транзакций этого типа передач. Аналогично, 10% пропускной способности канала, гарантированных для управляющих передач, означают, что в каждом кадре 10% времени гарантированно могут занять транзакции управляющих передач.
Далее ещё раз внимательно посмотрите на рисунок выше. На рисунке я не случайно выделил небольшие интервалы в начале и в конце каждого кадра. В реальности, в начале и конце каждого кадра тоже выделяются небольшие интервалы времени, которые используются специальным образом.
Начало каждого кадра помечается посылкой специального маркер-пакета SOF (start of frame), в состав которого входят 11 младших бит номера кадра. Этот маркер-пакет используется для синхронизации изохронных точек и хабов. В режиме HS каждый кадр делится на 8 микрокадров по 125 мкс, каждый из которых начинается с посылки маркер-пакета SOF (при этом в SOF всех микрокадров, относящихся к одному кадру, передаётся одинаковый номер).
Интервал времени в конце каждого кадра называется EOF. EOF — это время тишины. До наступления этого времени должны быть завершены все транзакции. Если в это время хаб обнаружит, что в какой-то нисходящий порт ему сыпят данные, то он этот порт просто отключит и сообщит об этом хосту.
Теперь вернёмся к транзакциям и разберём более подробно, что же происходит во время сеанса связи с конечной точкой и из чего состоят транзакции.
Сразу отвечу на второй поставленный нами вопрос — транзакции состоят из пакетов. Если помните, мы уже говорили, что любые сеансы обмена данными могут начинаться только по инициативе хоста. Так вот, любая транзакция начинается хостом. И начинается она с отправки маркер-пакета транзакции, в котором указывается адрес устройства, адрес конечной точки, с которой хост хочет «пообщаться», а также направление передачи данных. Получив такой пакет, адресуемое устройство готовится к обмену. Далее, после небольшого таймаута, следует пакет данных от источника (источник определяется направлением, указанным в маркер-пакете). Для изохронных передач транзакция на этом заканчивается (поскольку им не нужно никаких подтверждений доставки данных). Для всех остальных типов передач в транзакцию входит ещё третий пакет — пакет подтверждения или пакет квитирования (handshake). Для наглядности структура транзакций показана на рисунке ниже.
Далее подробнее поговорим про пакеты. Всего существует 4 типа пакетов: маркер-пакеты (token), пакеты данных (data), пакеты подтверждения (handshake) и специальные пакеты (special). Эти пакеты имеют строго определённую структуру, которая зависит от типа пакета, хотя у всех типов пакетов можно выделить и некоторые общие поля. Общая структура пакетов показана на рисунке справа (для скоростей передачи LS/HS). Пакет можно условно разделить на заголовок (2 байта), имеющий общую для всех пакетов структуру (Sync+PID+Check), и тело, защищённое контрольной суммой. Наличие, размер и структура тела, а также количество бит контрольной суммы зависят от типа пакета.
Итак, любой пакет на LS/FS начинается с 8 бит синхронизации — поле Sync. Для синхронизации используется битовая последовательность b’10000000′. (Для HS длина поля синхронизации составляет 32 бита.)
Далее следует 4 бита идентификатора пакета — PID и его инверсная копия — Check. PID определяет назначение пакета и, соответственно, его последующую структуру. В таблице ниже представлено небольшое описание различных идентификаторов пакетов шины USB.
Остальные специальные пакеты не будем рассматривать, поскольку они нам пока не понадобятся.
Идём дальше. Во всех полях пакетов, кроме поля CRC, данные передаются младшим битом вперёд.
Все пакеты состоят из целого числа байт (разрядность полей, входящих в пакет, специально так подобрана, чтобы сумма разрядов всех этих полей была кратна восьми).
Все пакеты заканчиваются специальным сигналом EOP, для LS/FS этот сигнал длится 2 битовых интервала (позже, когда дойдём до физики, мы рассмотрим, как формируется этот сигнал), для HS — таким специальным сигналом является передача определённой последовательности бит.
Передаваемые по шине данные кодируются по методу NRZI с техникой вставки бит (bit stuffing). Расшифруем что это значит. Это значит, что при передаче нулевого бита состояние сигнала на шине меняется на противоположное, а при перередаче единицы — состояние сигнала не меняется. Вставка бит используется для того, чтобы не потерять синхронизацию при монотонном единичном сигнале. Суть этой вставки сводится к тому, что после каждых 6 подряд единиц в передаваемые данные вставляется нулевой бит, независимо от того, какое значение имеет бит, следующий за этой группой единиц. (чтобы использовать NRZI и технику bit staffing — придётся разработать специальную процедурку для перекодирования передаваемых и принимаемых данных. )
Кроме того, нам нужно уметь вычислять CRC5 и CRC16. Вычисление CRC вообще отдельная тема, про неё подробно написано тут. А вот тут можно найти специальные процедурки для вычисления наших CRC5 и CRC16 .
Теперь поговорим про интервал ожидания (помните, на рисунке со структурой транзакций, между пакетами нарисованы небольшие интервалы). Вот давайте поговорим, откуда они и зачем. Как все мы понимаем, сигнал не может дойти от источника до приёмника мгновенно. Во-первых, задержку вносят кабели, во-вторых, задержку вносят хабы (они, как мы помним, должны принять сигнал от источника и ретранслировать его приёмнику), в-третьих, нужно учитывать, что хабов в цепочке от хоста до устройства может быть несколько, ну и наконец, нужно понимать, что приёмник должен иметь некоторое время, чтобы «осмыслить» принятый пакет, решить есть ли в нём ошибки, кому он предназначен и т.д.
Таким образом, ответный пакет источник не может получить мгновенно, потому что приёмнику нужно время чтобы «подумать» над ответом, и всей этой транспортной сети нужно время чтобы «доставить» ответ. С другой стороны, ответ нельзя ждать бесконечно долго, вдруг он вообще не придёт.
Поэтому в устройствах USB нормируется, во-первых, «максимальное время оборота по шине» — это время, за которое данные должны добежать до приёмника и вернуться назад к источнику в самом худшем случае — при прохождении последовательно через 5 хабов (время на то, чтобы «доставить»), и, во-вторых, «максимальная задержка ответа» — максимальное время от конца увиденного EOP до начала передачи ответного пакета (время на то, чтобы «подумать»). В устройствах при ожидании пакета запускаются специальные таймеры, которые отсчитывают интервал, достаточный для формирования ответа и его доставки, и если ответ за это время не получен — это воспринимается как ошибка.
Для FS интервал ожидания составляет 16-18 битовых интервалов, для HS — 736-816 битовых интервалов. Максимальное время, за которое мы должны всё обдумать и начать посылать ответ, составляет 7,5 битовых интервалов на FS и 192 битовых интервала для HS.
Ну и раз уж мы заговорили про битовые интервалы, то следует сказать, что длительность битового интервала для скорости LS составляет примерно 667 нс (1,5 Мбит/с), для FS примерно 83 нс, для HS примерно 2 нс.
Ещё один интересный вопрос. Зачем придумали аж целых 4 идентификатора пакетов данных? Сделали это вот почему. При передачах типа bulk (массивы), control (управляющие) и interrupt (прерывания) приёмник после получения пакета данных должен послать назад к источнику пакет подтверждения. Этот пакет подтверждения (так же, как и сам пакет данных) может испортиться. А если источник не получит подтверждения успешной передачи пакета данных, то в следующей транзакции он повторит отправку отправку этого пакета. Чтобы приёмник понял, что он эти данные уже получал, пакеты данных посылают с чередующимся PID. Чётные пакеты посылают с PID Data0, а нечётные — с PID Data1. Таким образом, получив два раза пакет данных с одним и тем же PID, приёмник понимает, что это не какие-то новые данные, а просто повторная отправка предыдущего пакета, потому что источник в предыдущей транзакции не увидел пакет подтверждения. Специальный бит в конечной точке, который указывает, пакет данных с каким PID мы ждём, называется Toggle Bit.
Ладно, с Data0, Data1 всё ясно, а для чего нужны PID Data2 и MData? Да примерно для того же самого. Они позволяют различить пакеты данных внутри микрокадра для широкополосных изохронных точек (USB2.0).
Ну, на этом пожалуй хватит про всякие транзакции и пакеты (если что — потом допишем), перейдём теперь к самому низкому уровню реализации — к физике.
С точки зрения физики в USB всё достаточно просто. Для связи по USB используются 4 провода: +5В, D+, D- и GND. Эти провода имеют стандартную цветовую маркировку: красный провод — это +5В, чёрный — GND, зелёный — D+, белый — D-.
Для передачи битов используется дифференциальный сигнал между проводами D+ и D-. Провода +5В и GND используются для питания устройства, а так же для индикации некоторых специальных состояний (вместе с D+ и D-).
На линиях D+ и D- высокий уровень соответствует напряжению +3,3 В (от 2,7 до 3,6).
Дифференциальный сигнал, при котором разница между D+ и D- больше 200 мВ при уровне напряжения на линии D+ > 2В называется Diff1.
Дифференциальный сигнал, при котором разница между D- и D+ больше 200 мВ при уровне напряжения на линии D- > 2В называется Diff0.
Состояние, когда на обоих сигнальных линиях присутствует низкий уровень относительно GND (D+
Шина USB обеспечивает обмен данными между хост-компьютером и множеством периферийных устройств (ПУ). USB является единой централизованной аппаратно-программной системой массового обслуживания множества устройств и множества прикладных программных процессов. Связь программных процессов со всеми устройствами обеспечивает хост-контроллер с многоуровневой программной поддержкой. Этим USB существенно отличается от традиционных периферийных интерфейсов (портов LPT, COM, GAME, клавиатуры, мыши и т. п.), сравнение этих типов подключений приводится в таблице.
Таблица. Сравнение шины USB с традиционными периферийными интерфейсами
Традиционные интерфейсы (COM, LPT, Game…) | Шина USB |
Подключение каждого устройства в общем случае требует присутствия собственного контроллера (адаптера) 1 | Все устройства подключены через один хостконтроллер |
Каждый контроллер занимает свои ресурсы (области в пространстве памяти, ввода/вывода, а также запросы прерывания) | Ресурсы занимает только хост-контроллер |
Малое количество устройств, которые возможно одновременно подключить к компьютеру | Возможность подключения до 127 устройств |
Драйверы устройств могут обращаться непосредственно к контроллерам своих устройств, независимо друг от друга | Драйверы устройств обращаются только к общему драйверу хост-контроллера |
Независимость драйверов оборачивается непредсказуемостью результата одновременной работы с множеством устройств, отсутствием гарантий качества обслуживания (возможность задержек и уменьшения скорости передачи) для различных устройств | Централизованный планируемый обмен обеспечивает гарантии качества обслуживания, что позволяет передавать мультимедийные изохронные данные наряду с обычным асинхронным обменом |
Разнообразие интерфейсов, разъемов и кабелей, специфичных для каждого типа устройств | Единый удобный и дешевый интерфейс для подключения устройств всех типов. Возможность выбора скорости работы устройства (1,5–15–480 Мбит/с) в зависимости от потребности |
Отсутствие встроенных средств обнаружения подключения/отключения и идентификации устройств, сложность поддержки PnP | Возможность «горячего» подключения/отключения устройств, полная поддержка PnP, динамическое конфигурирование |
Отсутствие средств контроля ошибок | Встроенные средства обеспечения надежной передачи данных |
Отсутствие штатного питания устройств | Возможность питания устройств от шины, а также наличие средств управления энергопотреблением |
1 — Возможностью подключения к одному контроллеру множества устройств обладает и шина SCSI, но ее параллельный интерфейс по сравнению с USB слишком дорог, громоздок и более ограничен в топологии.
Кадры и микрокадры
Хост организует обмены с устройствами согласно своему плану распределения ресурсов. Для этого хост-контроллер циклически с периодом 1 мс формирует кадры (frames), в которые укладываются все запланированные транзакции (cм. рисунок ниже). Каждый кадр начинается с посылки пакета-маркера SOF (Start Of Frame), который является синхронизирующим сигналом для изохронных устройств, а также для хабов. Кадры нумеруются последовательно, в маркере SOF передаются 11 младших бит номера кадра. В режиме HS каждый кадр делится на 8 микрокадров, и пакеты SOF передаются в начале каждого микрокадра (с периодом 125 мкс). При этом во всех восьми микрокадрах SOF несет один и тот же номер кадра; новое значение номера кадра передается в нулевом микрокадре. В каждом микрокадре может быть выполнено несколько транзакций, их допустимое число зависит от скорости, длины поля данных каждой из них, а также от задержек, вносимых кабелями, хабами и устройствами. Все транзакции кадров должны быть завершены до начала интервала времени EOF (End of Frame). Период (частота) генерации микрокадров может немного варьироваться с помощью специального регистра хост-контроллера, что позволяет подстраивать частоту для изохронных передач.
Кадрирование используется и для обеспечения живучести шины. В конце каждого микрокадра выделяется интервал времени EOF (End Of Frame), на время которого хабы запрещают передачу по направлению к контроллеру. Если хаб обнаружит, что с какого-то порта в это время ведется передача данных (к хосту), этот порт отключается, изолируя «болтливое» устройство, о чем информируется USBD.
Счетчик микрокадров в хост-контроллере используется как источник индекса при обращении к таблице дескрипторов кадров. Обычно драйвер USB составляет таблицу дескрипторов для 1024 последовательных кадров1, к которой он обращается циклически. С помощью этих дескрипторов хост планирует загрузку кадров так, чтобы кроме запланированных изохронных транзакций и прерываний в них всегда находилось место для транзакций управления. Свободное время кадров может заполняться передачами массивов. Спецификация USB позволяет занимать под периодические транзакции (изохронные и прерывания) до 90% пропускной способности шины, то есть времени в каждом микрокадре.
Сегодняшняя статья будет посвящена, как уже видно из названия, обсуждению основ интерфейса USB. Рассмотрим основные понятия, структуру данных, разберемся, как происходит передача, а в ближайшем будущем реализуем все это на практике. Приступаем!
Существует ряд различных спецификаций USB. Началось все с USB 1.0 и USB 1.1, затем интерфейс эволюционировал в USB 2.0, относительно недавно (на момент написания статьи) появилась окончательная спецификация USB 3.0. Но на данный момент наиболее распространенной является реализация USB 2.0. И для начала рассмотрим основные характеристики. Интерфейс USB 2.0 поддерживает три режима работы:
- High Speed - до 480 Мб/с
- Full Speed - до 12 Мб/с
- Low Speed - до 1.5 Мб/с
Командует на шине USB хост (например, ПК), к которому можно подключить до 127 различных устройств. Если этого мало, то нужно добавить еще один хост. Причем немаловажно, что устройство не может само послать/принять данные хосту/от хоста, необходимо, чтобы хост сам обратился к устройству.
Почти во всех статьях про интерфейс USB, которые я видел, используется термин "конечная точка", но о том, что это такое обычно написано довольно туманно. Так вот, конечная точка - это часть устройства USB, имеющая свой уникальный идентификатор. Каждое устройство может иметь несколько конечных точек. По большому счету - конечная точка - это всего лишь область памяти USB устройства, в которой могут храниться какие-либо данные (буфер данных). И в итоге мы получаем вот что - каждое устройство имеет свой уникальный адрес на шине USB, и при этом каждая конечная точка этого устройства также имеет свой номер.
Давайте немного отвлечемся и поговорим о "железной части" интерфейса. Существуют два типа коннекторов - Type A и Type B.
Как уже понятно из рисунка Type A всегда обращен к хосту. Именно такие разъемы мы можем наблюдать на ПК. Коннекторы Type B всегда относятся к подключаемым USB-устройствам. Кабель состоит из 4 проводов разных цветов. Ну, собственно, красный - это питание (+5 В), черный - земля, белый и зеленый предназначены для передачи данных.
Несмотря на то, что именно такая цветовая маркировка является наиболее распространенной, тем не менее некоторые производители могут использовать и другие цвета. Более того, у меня был кабель, в котором провода красного и черного цветов были перепутаны, то есть красный шел на землю. Так что, если оперируете непосредственно с внутренностями кабеля, будьте аккуратны и не доверяйте цвету безоговорочно. Лучше прозвонить и перепроверить.
Помимо изображенных на рисунке, существуют также другие варианты исполнения USB-коннекторов, например, mini-USB и другие, тут я думаю секрета никакого не открою.
Пожалуй, стоит коснуться способа передачи данных, но углубляться в это пока не будем. Итак, при передаче данных по шине USB используется принцип кодирования NRZI (без возврата к нулю с инверсией). Для передачи логической "1" необходимо повысить уровень линии D+ выше +2.8 В, а уровень линии D- понизить ниже +0.3 В. Для передачи нуля ситуация противоположная - (D- > 2.8 В) и (D+ < 0.3 В).
Отдельно стоит обсудить питание устройств. И тут также возможно несколько вариантов. Во-первых, устройства могут питаться от шины, тогда их можно разделить на два класса:
Разница тут заключается в том, что low-power устройства не могут потреблять больше, чем 100 мА. А устройства high-power должны потреблять не более 100 мА лишь на этапе конфигурации. После того, как они сконфигурированы хостом, их потребление может составлять до 500 мА. Кроме того, устройства могут иметь свой собственный источник питания. В этом случае они могут получать до 100 мА от шины, а все остальное забирать у своего источника.
С этим вроде бы все, так что логичным образом переходим к структуре передаваемых данных. Тем более это представляет для нас наибольший интерес.
Основные понятия
Архитектура USB допускает четыре базовых типа передач данных между хостом и периферийными устройствами:
Аппаратная часть USB включает:
- периферийные устройства USB, несущие полезные функции (USB-functions);
- хост-контроллер (Host Controller), обеспечивающий связь шины с центром компьютера, объединенный с корневым хабом (Root Hub), обеспечивающим точки подключения устройств USB. Существует два варианта хост-контроллеров USB 1.x — UHC (Universal Host Controller) и OHC (Open Host Controller), поддерживающие скорости FS/LS; высокую скорость шины USB 2.0 (HS и только) поддерживает EHC (Enhanced Host Controller);
- хабы USB (USB Hubs), обеспечивающие дополнительные точки подключения устройств;
- кабели USB, соединяющие устройства с хабами.
Программная часть USB включает:
- клиентское ПО (CSw, Client Software) — драйверы устройств USB, обеспечивающие доступ к устройствам со стороны прикладного ПО. Эти драйверы взаимодействуют с устройствами только через программный интерфейс с общим драйвером USB (USBD). Непосредственного обращения к каким-либо регистрам аппаратных средств драйверы устройств USB не выполняют;
- драйвер USB (USBD, USB Driver), «заведующий» всеми USB-устройствами системы, их нумерацией, конфигурированием, предоставлением служб, распределением пропускной способности шины, мощности питания и т. п.;
- драйвер хост-контроллера (HCD, Host Controller Driver), преобразующий запросы ввода/вывода в структуры данных, размещенные в коммуникационной области оперативной памяти, и обращающийся к регистрам хост-контроллера. Хост-контроллер выполняет физические транзакции, руководствуясь этими структурами данных.
Драйверы USBD и HCD составляют хост-часть ПО USB; спецификация USB очерчивает круг их задач, но не описывает интерфейс между ними. Физическое устройство USB должно иметь интерфейс USB, обеспечивающий полную поддержку протокола USB, выполнение стандартных операций (конфигурирование и сброс) и предоставление информации, описывающей устройство. Физические устройства USB могут быть комбинированными (compound devices): включать в себя несколько устройств-функций, подключенных к внутреннему хабу, а также предоставлять своим внутренним хабом дополнительные внешние точки подключения.
Работой всех устройств шины USB управляет хост-контроллер (host controller), являющийся программно-аппаратной подсистемой хост-компьютера. Хост-контроллер является интеллектуальным устройством шины PCI или составной частью «южного» хаба (моста) системной платы, интенсивно взаимодействующим с оперативной памятью.
Физическая топология шины USB — многоярусная звезда (см. рисунок, а). Ее вершиной является хост-контроллер, объединенный с корневым хабом (root hub). Хаб является устройством-разветвителем, он может служить и источником питания для подключенных к нему устройств. К каждому порту хаба может непосредственно подключаться периферийное устройство или промежуточный хаб; шина допускает до пяти уровней (ярусов) каскадирования хабов (не считая корневого). Поскольку комбинированные устройства содержат внутри себя хаб, их подключение к хабу пятого яруса уже недопустимо. Каждый промежуточный хаб имеет несколько нисходящих (downstream) портов для подключения периферийных устройств (или нижележащих хабов) и один восходящий (upstream) порт для подключения к корневому хабу или нисходящему порту вышестоящего хаба.
Логическая топология USB — звезда. Хабы (включая корневой) создают иллюзию непосредственного подключения каждого логического устройства к хост-контроллеру (см. рисунок ниже, б). В этой звезде устанавливаются сугубо подчиненные отношения по системе опроса-ответа: хост-контроллер по своей инициативе передает данные к выбранному устройству или принимает их. Устройство по своей инициативе передавать данные не может; непосредственные передачи данных между устройствами невозможны. Устройство по своей инициативе может лишь сигнализировать о «пробуждении» (wakeup), для чего используется специальная сигнализация, но не передача данных.
Физический интерфейс USB прост и изящен. Конструкция кабелей и коннекторов USB не дает возможности ошибиться при подключении устройств (см. рисунок ниже, а и б). Для распознавания разъема USB на корпусе устройства ставится стандартное символическое обозначение (см. рисунок ниже, в). Гнезда типа «A» устанавливаются только на нисходящих портах хабов, вилки типа «A» — на шнурах периферийных устройств или восходящих портов хабов. Гнезда и вилки типа «B» используются только для шнуров, отсоединяемых от периферийных устройств и восходящих портов хабов (от «мелких» устройств — мышей, клавиатур и т. п. кабели, как правило, не отсоединяются). Для малогабаритных устройств имеются разъемы mini-B, а для поддержки OTG (On-the-Go) имеются и вилки mini-A, и розетки miniAB. Хабы и устройства обеспечивают возможность «горячего» подключения и отключения с сигнализацией об этих событиях хосту.
При планировании соединений следует учитывать способ питания устройств: устройства, питающиеся от шины, как правило, подключают к хабам, питающимся от сети. К хабам, питающимся от шины, подключают лишь маломощные устройства — так, к клавиатуре USB, содержащей внутри себя хаб, подключают мышь USB и другие устройства-указатели (трекбол, планшет).
Логическое устройство USB представляет собой набор независимых конечных точек (Endpoint, EP), с которыми хост-контроллер (и клиентское ПО) обменивается информацией. Каждому логическому устройству USB (как функции, так и хабу) конфигурационная часть ПО хоста назначает свой адрес (1–127), уникальный на данной шине USB. Каждая конечная точка логического устройства идентифицируется своим номером (0–15) и направлением передачи (IN — передача к хосту, OUT — от хоста). Точки IN4 и OUT4, к примеру, представляют собой разные конечные точки, с которыми могут общаться даже модули клиентского ПО. Набор конечных точек зависит от устройства, но всякое устройство USB обязательно имеет двунаправленную конечную точку 0 (EP0), через которую осуществляется его общее управление. Для прикладных целей используются конечные точки с номерами 1–15 (1–2 для низкоскоростных устройств). Адрес устройства, номер и направление конечной точки однозначно идентифицируют приемник или источник информации при обмене хост-контроллера с устройствами USB. Каждая конечная точка имеет набор характеристик, описывающих поддерживаемый тип передачи данных (изохронные данные, массивы, прерывания, управляющие передачи), размер пакета, требования к частоте обслуживания.
Устройство может выполнять несколько различных функциональных задач: например, привод CD-ROM может обеспечивать проигрывание аудиодисков и работать как устройство хранения данных. Для решения каждой задачи в устройстве определяется интерфейс — набор конечных точек, предназначенных для выполнения данной задачи, и правила их использования. Таким образом, каждое устройство должно обеспечивать один или несколько интерфейсов. Наличие нескольких интерфейсов позволяет нескольким драйверам, каждый из которых обращается только к своему интерфейсу (представляющему часть устройства USB), работать с одним и тем же устройством USB. Каждый интерфейс может иметь один или несколько альтернативных вариантов (альтернативных установок — alternate settings), из которых в данный момент активным может быть только один. Варианты различаются наборами (возможно, и характеристиками) используемых конечных точек.
Набор одновременно поддерживаемых интерфейсов составляет конфигурацию устройства. Устройство может иметь одну или несколько возможных конфигураций, из которых на этапе конфигурирования хост выбирает одну, делая ее активной. От выбранной конфигурации зависит доступная функциональность, и зачастую — потребляемая мощность. Пока устройству не назначен номер выбранной конфигурации, оно не может функционировать в прикладном смысле и ток потребления от шины не должен превышать 100 мА. Хост выбирает конфигурацию исходя из доступности всех ресурсов, затребованных данной конфигурацией, включая и ток потребления от шины.
Модель передачи данных
Каждая единица клиентского ПО (обычно представляемая драйвером) связывается с одним интерфейсом своего устройства (функции) монопольно и независимо (см. рисунок ниже). Связи на этом рисунке обозначают коммуникационные каналы (communication pipes), которые устанавливаются между драйверами устройств и их конечными точками. Каналы устанавливаются только с конечными точками устройств, относящимися к выбранным (из альтернативных) вариантам интерфейсов активной конфигурации. Другие конечные точки недоступны.
Каналы
Коммуникационные каналы USB разделяются на два типа:
С каналами связаны характеристики, соответствующие конечной точке (полоса пропускания, тип сервиса, размер буфера и т. п.). Каналы организуются при конфигурировании устройств USB. Полоса пропускания шины делится между всеми установленными каналами. Выделенная полоса закрепляется за каналом, и если установление нового канала требует такой полосы, которая не вписывается в уже существующее распределение, запрос на выделение канала отвергается.
Каналы различаются и по назначению:
Интерфейс устройства, с которым работает клиентский драйвер, представляет собой связку клиентских каналов (pipe’s bundle). Для этих каналов драйверы устройств являются единственными источниками и потребителями передаваемых данных.
Структура данных интерфейса USB.
Вся информация передается кадрами, которые отправляются через равные промежутки времени. В свою очередь каждый кадр состоит из транзакций::
Каждый кадр включает в себя пакет SOF (Start Of Frame), затем следуют транзакции для разных конечных точек, и завершается все это пакетом EOF (End Of Frame). Если говорить совсем точно, то EOF - это не совсем пакет в привычном понимании этого слова - это интервал времени, в течение которого обмен данными запрещен.
Каждая транзакция имеет следующий вид:
Первый пакет (его называют Token-пакет) содержит в себе информацию об адресе устройства USB, а также о номере конечной точки, которой предназначена эта транзакция. Кроме того, в этом пакете хранится информация о типе транзакции (какие бывают типы мы еще обсудим, но чуть позже). Data-пакет - с ним все понятно, это данные, которые передает хост, либо конечная точка (зависит от типа транзакции). Последний пакет - Status - предназначен для проверки успешности получения данных.
Уже очень много раз прозвучало слово "пакет" применительно к интерфейсу USB, так что следует разобраться, что он из себя представляет. Начнем с пакета Token:
Пакеты Token бывают трех типов:
Пакет In сообщает USB-устройству, что хост готов принять от него информацию. Пакет Out, напротив, сигнализирует о готовности и желании хоста поделиться информацией. Пакет Setup нужен для использования управляющих передач. Ну а пакет Start Of Frame используется для того, чтобы инициировать начало кадра.
В зависимости от типа пакета значение поля PID в Token-пакете может принимать следующие значения:
- Token пакет типа OUT - PID = 0001
- Token пакет типа IN - PID = 1001
- Token пакет типа SETUP - PID = 1101
- Token пакет типа SOF - PID = 0101
Кроме того, есть еще один немаловажный нюанс. Поле PID включает в себя 4 бита, но при передаче они дополняются еще 4-мя битами, которые получаются путем инвертирования первых 4-ых.
Переходим к следующей составной части пакета Token - поля Address и Endpoint - в них содержатся адрес USB устройства и номер конечной точки, которой предназначена транзакция. И, наконец, поле CRC - это контрольная сумма, с этим понятно. Она как и обычно используется для контроля целостности и безошибочности данных.
На очереди Data пакет - то есть пакет данных:
Тут все, в принципе, так же, как и в пакете Token, только вместо адреса устройства и номера конечной точки здесь в наличии передаваемые данные. Таким образом, осталось рассмотреть Status пакеты и пакеты SOF, начинаем с первого из них:
Тут PID может принимать всего лишь два значения:
- Пакет принят корректно - PID = 0010
- Ошибка при приеме пакета - PID = 1010
Start Of Frame пакет:
Видим новое поле Frame - оно содержит в себе номер передаваемого кадра. Давайте в качестве примера рассмотрим процесс записи данных в USB-устройство, то есть полную структуру кадра для записи. Кадр, как вы помните состоит из транзакций и имеет следующий вид:
Что представляют из себя все эти транзакции? Сейчас разберемся, транзакция SETUP:
Аналогично при чтении данных из USB-устройства кадр выглядит так:
Транзакцию SETUP мы уже видели, посмотрим на транзакцию IN:
Как видите, все эти транзакции имеют именно такую структуру, как мы обсуждали выше. В общем, думаю достаточно на сегодня, довольно-таки длинная статья получилась, в ближайшее время обязательно будем использовать данные теоретические аспекты на практике 👍
Итак, начнём мы с того, что спецификацией определены для USB-устройств 6 состояний, в которых они могут находиться. В каждом из этих состояний наше устройство должно себя адекватно вести и уметь переходить из одного состояния в другое. Состояния могут быть такими:
Подключено (attached) — устройство подключено к хабу, но питание от шины не подано. В этом случае устройство никак не может себя проявить. Для устройств с внешним питанием такое состояние отсутствует.
Запитано (powered) — в это состояние устройство переходит после того, как хаб его запитает. В таком состоянии устройство может заявить о себе, подтягивая резистором линию D+ или D- к шине питания. Понятно, что в первом состоянии мы ничего и не сможем сделать, если питаемся от шины (как можно себя проявить, если у тебя нет питания). Во втором состоянии подтяжка происходит автоматически. У нас просто в устройстве впаян резистор, который подтягивает нужную линию к питанию, как только это питание появится. То есть в этих двух состояниях нам особо ничего и делать-то не нужно, всё происходит само.
Дежурное состояние (default state) — после того, как нам подали питание и мы заявили о своём присутствии на шине — мы должны сразу перейти в дежурное состояние. В этом состоянии устройство должно отзываться на нулевой адрес, причём только на обращения к точке EP0, а также потреблять от шины не более 100 мА.
Адресовано (addressed) — в это состояние устройство переходит после того, как запросом Set_Address ему будет присвоен уникальный адрес на шине. В таком состоянии устройство должно отзываться только на присвоенный ему уникальный адрес, но также только при обращениях к точке EP0, и также потреблять от шины не более 100 мА.
Сконфигурировано (configured) — в это состояние устройство переходит после того, как запросом Set_Configuration для него установлена рабочая конфигурация. В таком состоянии устройство откликается только на присвоенный ему уникальный адрес, но уже при обращении ко всем точкам, описанным в установленной конфигурации, и может потреблять от шины заявленный ток.
Приостановлено (suspended) — в это состояние устройство должно переходить автоматически при отсутствии активности шины. При этом устройству запрещается что-либо передавать в шину, а также запрещается потреблять от шины более 2,5 мА. Единственное, что устройство может сделать — это послать в порт сигнал удалённого пробуждения, но только в том случае, если ранее ему специальным управляющим запросом разрешили подавать такой сигнал. При пробуждении устройства (причём не важно, кем оно инициировано), в порт, к которому оно подключено, хаб в течении 20 мс транслирует специальный сигнал возобновления, завершающийся сигналом LS-EOP. В приостановленном состоянии устройство сохраняет выбранную конфигурацию, присвоенный адрес и все остальные настройки.
Что нам делать в состоянии запитано мы уже обсудили. А вот для того, чтобы адекватно себя вести в дежурном состоянии, а также в состояниях адресовано, сконфигурировано и приостановлено — нам придётся отвечать на запросы и сигналы от хоста, поэтому придётся разобраться, какие это могут быть запросы и как нам на них реагировать.
Изучать запросы мы, естественно, начнём со стандартных запросов. Эти запросы должны поддерживать все USB-устройства. Но прежде чем их рассматривать, давайте кое-что вспомним (чтобы пазл полностью сложился).
Вспомним мы вот что. Ранее (в первой части) мы говорили, что для управления устройствами используются управляющие передачи (control transfer). А поскольку обсуждаемые нами стандартные запросы и ответы — это и есть ни что иное, как управление устройством, то формируются они именно в формате управляющих передач. Сейчас, зная о пакетах и транзакциях, мы можем рассмотреть формат этих передач более подробно.
Итак, управляющая передача состоит из 2-х или 3-х стадий: стадии установки (Setup Stage), стадии передачи данных (Data Stage), которая в некоторых запросах может отсутствовать, и стадии состояния (Status Stage).
Стадия Setup Stage состоит из транзакции управления, в пакете данных которой хост передаёт устройству сам запрос.
Стадия Data Stage состоит из одной или нескольких транзакций ввода или вывода, в которых передаются дополнительные данные запроса или ответ устройства. Как уже было сказано выше, эта стадия может отсутствовать.
Стадия Status Stage состоит из пустой транзакции ввода или вывода и служит для определения успешности или неуспешности завершения управляющей передачи.
Чтобы стало понятнее — внимательно смотрим рисунок ниже, на котором показаны варианты того, как могут выглядеть управляющие передачи.
Пакет данных транзакции управления всегда должен иметь PID Data0. Далее идентификаторы Data1, Data0 чередуются, но пакет данных передаваемый на стадии Status Stage должен иметь идентификатор Data1, независимо от того, какой идентификатор использовался перед этим.
На стадии Status Stage устройство при успешном выполнении запроса отвечает пустым пакетом Data1 на транзакцию IN или пакетом Ack на транзакцию OUT. Если оно ещё не завершило выполнение запроса, то оно отвечает пакетом NAK. Если при выполнении запроса произошла ошибка, то устройство отвечает пакетом STALL (для управляющих конечных точек такая ситуация не требует вмешательства хоста и не вызывает блокировки канала).
Далее давайте рассмотрим более подробно из каких полей состоит пакет данных, передаваемый на стадии Setup Stage:
Байт стандартных характеристик содержит следующую информацию:
- старший бит показывает направление передачи данных (0 — от хоста, 1 — от устройства)
- биты 6,5 определяют тип запроса (00 — стандартный, 01 — для класса, 10 — специфичный, 11 — зарезервировано)
- младшие 5 бит определяют получателя (00000 — устройство целиком, 00001 — интерфейс, 00010 — конечная точка, 00011 — другой, остальные значения зарезервированы)
С размером и форматом полей будет легче ориентироваться, если вы запомните маленькую хитрость, придуманную программистами для облегчения своего труда. Первые буквы названия полей (до заглавной) означают следующее:
bm — сокращение от bit mask, такие первые буквы названия означают, что значение имеют отдельные биты поля, то есть поле рассматривается побитово;
b — сокращение от byte, такое поле рассматривается целиком и имеет размер 1 байт
w — сокращение от word, такое поле также рассматривается целиком и имеет размер 2 байта
В дальнейшем я буду битовые поля писать в формате 0bBBBBBBBB, поля размером 1 байт — в формате 0xHH, а двухбайтовые поля — в формате 0xHHHH
Этот запрос используется для получения состояния устройства USB, интерфейса или конечной точки. При этом остальные поля запроса могут принимать следующие значения:
- bmRequestType
- 0b10000000 — получить состояние USB-устройства
- 0b10000001 — получить состояние интерфейса
- 0b10000010 — получить состояние конечной точки
- Как видите, для всех трёх вариантов старший бит равен 1, то есть на стадии передачи данных устройство должно передавать данные хосту (отвечать на этот запрос).
- wValue
- 0x0000
- wIndex
- 0x0000 — если запрос обращён к USB-устройству
- если запрос обращён к интерфейсу или к конечной точке, то в этом поле указывается номер интерфейса или конечной точки
- wLength
- 0x0002 — то есть, в ответ на этот запрос, хост, на стадии передачи данных, должен получить от устройства 2 байта информации (слово состояния). Соответственно, если запрос был адресован USB-устройству, то хост получает слово состояния устройства, если запрос был адресован интерфейсу — слово состояния интерфейса, если конечной точке — слово состояния конечной точки. Как видите, всё не так уж и сложно.
Слово состояния устройства имеет следующий формат:
- бит 0 — режим питания устройства: 0 — питание от шины, 1 — от внешнего источника
- бит 1 — реакция на сигнал пробуждения (remote wakeup): 0 — устройство игнорирует сигнал пробуждения, 1 — устройство реагирует на этот сигнал
- биты 2..15 не используются и должны содержать нули
Слово состояния интерфейса всегда должно быть равно нулю (все его биты равны нулю).
Слово состояния конечной точки имеет следующий формат:
Этот запрос используется для запрета какого-либо свойства или состояния. При этом остальные поля запроса могут принимать следующие значения:
- bmRequestType
- 0b00000000 — сбросить свойство USB-устройства
- 0b00000001 — сбросить свойство интерфейса
- 0b00000010 — сбросить свойство конечной точки
- wValue
- В этом поле содержится код свойства, которое нужно сбросить.
- wIndex
- 0x0000 — если запрос обращён к USB-устройству
- если запрос обращён к интерфейсу или к конечной точке, то в этом поле указывается номер интерфейса или конечной точки
- wLength
- 0x0000 — то есть, на стадии передачи данных никаких данных передавать не нужно и управляющая передача с таким запросом будет состоять только из двух стадий.
Этот запрос используется для разрешения какого-либо свойства или состояния. При этом остальные поля запроса могут принимать следующие значения:
Этот запрос используется для назначения USB-устройству адреса на шине USB. При этом остальные поля запроса могут принимать следующие значения:
Этот запрос используется для получения дескрипторов. Подробнее о дескрипторах и их форматах мы поговорим позднее, а пока скажу лишь, что это специальные структуры данных, содержащие описание устройства, его интерфейсов, конечных точек и т.д. При этом остальные поля запроса могут принимать следующие значения:
- bmRequestType
- 0b10000000 — получить дескриптор устройства
- 0b10000001 — получить дескриптор интерфейса
- 0b10000010 — получить дескриптор конечной точки
- 0x01 — стандартный дескриптор устройства
- 0x02 — дескриптор конфигурации
- 0x03 — дескриптор строки
- 0x04 — дескриптор интерфейса
- 0x05 — дескриптор конечной точки
- 0x06 — уточняющий дескриптор устройства (для HS-устройств)
- 0x07 — дескриптор дополнительной конфигурации
- 0x08 — дескриптор управления питанием интерфейса
- 0x09 — дескриптор OTG
- 0x0A — отладочный дескриптор
- 0x0B — дополнительный дескриптор интерфейса
- wIndex
- В дескрипторах строк в этом поле содержится идентификатор языка, для остальных типов дескрипторов это поле должно быть равно нулю
- wLength
- Максимальный размер дескриптора в байтах. Если реальный размер запрашиваемого дескриптора меньше этого значения, то устройство должно просто целиком отослать запрашиваемый дескриптор, а если реальный размер дескриптора больше, то устройство должно послать хосту только первые wLength байт этого дескриптора
Этот запрос используется для изменения существующих или добавления новых дескрипторов. Остальные поля могут принимать такие же значения, как и в запросе Get_Descriptor, только старший бит поля bmRequestType в этом случае будет равен нулю, поскольку данные на стадии передачи данных будут передаваться от хоста к устройству.
Этот запрос используется для того, чтобы узнать номер активной (выбранной в данный момент) конфигурации. При этом остальные поля запроса могут принимать следующие значения:
Этот запрос используется для того, чтобы выбрать новую активную конфигурацию для USB-устройства. При этом остальные поля запроса могут принимать следующие значения:
Этот запрос используется для того, чтобы узнать какая альтернативная установка в настоящий момент используется для указанного интерфейса:
Этот запрос используется для того, чтобы выбрать новую альтернативную установку для указанного интерфейса. При этом остальные поля запроса могут принимать следующие значения:
- bmRequestType
- 0b00000001
- wValue
- 0x00NN, где NN — номер альтернативной установки, которую мы хотим выбрать
- wIndex
- 0xNNNN, где NNNN — номер интерфейса, для которого мы выбираем новую альтернативную установку
- wLength
- 0x0000
На этом пока всё. Для того, чтобы приступать к написанию кода и экспериментам, нам осталось обсудить только такие (уже всплывавшие несколько раз) понятия, как «дескрипторы» и «классы». Этим мы и займёмся в следующей статье.
В настоящий момент один из самых популярных интерфейсов — это безусловно USB. Девайсов, которые его используют, просто огромное количество. Это и мышки, и клавиатуры, и принтеры, и сотовые телефоны, и много чего ещё. В отличии от стремительно исчезающего RS-232, USB встречается во всех современных компьютерах, ноутбуках, телефонах… так что, если мы хотим создавать действительно универсальные девайсы, придётся нам этот интерфейс изучать. Вот прямо сейчас и начнём, а заодно, по ходу изучения, попытаемся сами посоздавать каких-нибудь USB-девайсов.
Итак, USB (universal serial bus) — универсальная последовательная шина. Большинство USB-устройств соответствуют спецификациям 1.1 и 2.0. В спецификации 1.1 определены две скорости передачи информации: LS (low speed) — низкая скорость, 1,5 Мбит/с и FS (full speed) — полная скорость, 12 Мбит/с. В редакции 2.0 к ним добавлена ещё и высокая скорость HS (high speed), 480 Мбит/с. Не так давно вышла ещё спецификация — 3.0, но устройства, поддерживающие этот стандарт, пока не очень распространены, поэтому и бог с ней.
Физические устройства на шине USB бывают трёх типов: хост-контроллер, хаб и конечное устройство.
Хост-контроллер — это главный управляющий шиной USB. Именно он обеспечивает связь устройств, подключенных к шине, с компьютером (с ОС и с клиентским ПО). Любые сеансы обмена данными может начинать только хост-контроллер, остальные устройства молчат в тряпочку, пока хост-контроллер к ним не обратится.
Контроллер взаимодействует с ОС через драйвер хост-контроллера (HCD — host controller driver). Этот драйвер привязан к конкретной модели хост-контроллера. Только он знает какие данные, в какие регистры и в каком порядке пихать в хост-контроллер, а также откуда какие данные брать, чтобы хост-контроллер сделал то, чего от него хотят.
Со стороны ОС шиной USB управляет ещё один драйвер — USBD (universal serial bus driver). Ему совершенно пофиг, как там конкретно реализован хост-контроллер и где у него какие регистры (для этого есть HCD), USBD решает общие (неспецифические для конкретного хост-контроллера) вопросы: взаимодействие с клиентским ПО, нумерация устройств на шине, их конфигурирование, распределение питания и пропускной способности шины и так далее. Это, можно сказать, своеобразный диспетчер, который осуществляет общий контроль над шиной и её взаимодействие с внешним миром (с клиентским ПО).
Хост-контроллер — птица гордая и пугливая, поэтому непосредственно ни с кем из подданных он не разговаривает. Для общения с подданными у него есть специальные помощники — хабы (их ещё иногда называют концентраторами).
Хабы — это устройства, которые позволяют физически подключить устройства USB к шине. Они предоставляют порты для подключения, ретранслируют трафик от хост-контроллера к конечным устройствам и обратно, отслеживают состояние и физически управляют электропитанием портов. У хабов есть один восходящий (upstream) порт, — это тот порт, который подключен по направлению к хост-контроллеру, и несколько нисходящих (downstream) портов, — это порты, к которым подключаются конечные устройства. Хабы можно каскадировать, подключая к нисходящему порту хаба ещё один хаб. Самый главный хаб, интегрированный с хост-контроллером, называется корневым хабом (он же — корневой концентратор или root hub).
Другими словами можно сказать, что у хаба есть две основных задачи: 1) создать хост-контроллеру иллюзию, что он непосредственно разговаривает с подключенным к хабу устройством; 2) наблюдать за своим сегментом шины (за девайсами, подключенными к нисходящим портам), сообщать «наверх» обо всех изменениях и, если надо, — подключать и отключать питание портов.
Конечные устройства — это все те полезные устройства, которые мы подключаем к шине USB (флэшки, принтеры, мышки и т.д.)
Нужно сказать, что физические устройства и логические устройства — это не всегда одно и тоже. Существуют, например, такие конечные устройства (называемые составными — compound devices), которые содержат внутри себя хаб, к которому подключено ещё несколько устройств. Несмотря на то, что в этом случае хаб и все, подключенные к нему устройства, запакованы в один корпус, с точки зрения логики шины это будут совершенно разные устройства.
Для логических конечных устройств обычно используют термин «функции». Таким образом, с точки зрения логики шины, устройства на ней можно разделить на хабы и функции (и неважно, запакованы ли они в один корпус или нет). Каждое логическое устройство на шине имеет уникальный адрес (1-127), присваеваемый ему хостом при подключении.
Исходя из описанного выше, получается, что физическая топология шины USB — дерево (ну, потому что хабы можно каскадировать), а логическая топология — звезда, центром которой является хост-контроллер. Физическая и логическая топологии шины USB показаны на рисунке ниже.
Идём дальше. Что же вообще представляет собой логическое устройство USB (как хабы, так и функции)?
Логическое устройство представляет собой набор так называемых конечных точек (endpoints или просто EP). Физически, конечные точки — это просто разные буферы в логическом устройстве USB, через которые происходит обмен данными с хостом. Логичный вопрос — а зачем нам иметь несколько буферов? Ну, просто потому что удобно для разных задач иметь разные буферы. Устройство же у нас может выполнять параллельно несколько разных задач. (Минимум две — отслеживать команды управления от хоста и делать что-то полезное.) У этих разных задач могут могут быть разные степени важности, требования к надёжности, своевременности и скорости доставки данных и, наконец, источники и потребители пересылаемой информации также могут быть разные (источником и потребителем полезной инфы обычно является клиентский драйвер, в то же время всякая управляющая инфа ему обычно нафиг не нужна).
Поскольку для решения описанных выше проблем недостаточно иметь просто разные буферы для разной передаваемой информации, то в дополнение к этому придумали ещё кое-что.
Во-первых, придумали 4 различных типа передач. Для каждой конечной точки должно быть определено, каким из этих типов передач с ней нужно общаться. Типы передач в USB существуют следующие:
Вернёмся к нашим конечным точкам. Для того, чтобы отличить одну точку от другой, — конечные точки, должны иметь уникальный номер. Но это не всё. Кроме номера, каждая конечная точка имеет ещё и направление. IN — если точка предназначена для передачи данных хосту, OUT — если точка предназначена для приёма данных от хоста. Точки с одинаковыми номерами, но с разными направлениями передачи данных — это разные с точки зрения логики шины конечные точки.
Единственное исключение — конечная точка EP0. У неё вообще особый статус. Она является служебной и предназначена для общего управления устройством (конфигурирование, настройка и т.д.). Кроме того, эта конечная точка двунаправленная и она должна обязательно присутствовать в любом USB-устройстве.
Исходя из всего вышеописанного, для идентификации какой-то конечной точки на шине, нам нужно знать адрес устройства, к которому относится конечная точка, её номер в устройстве и направление передачи данных через эту точку.
Поскольку устройство не всегда делает абсолютно всё на что оно только способно, да и способов решения одной и той же задачи оно может иметь несколько, то обычно нет необходимости задействовать абсолютно все конечные точки. Поэтому придумали такие понятия, как интерфейс, конфигурация и альтернативные установки. Интерфейс объединяет конечные точки, предназначенные для решения какой-либо одной задачи. Наборы используемых одновременно интерфейсов называются конфигурациями. Альтернативные установки позволяют включать или отключать какие-то входящие в конфигурацию конечные точки, в зависимости от способа решения задач для которых предназначена эта конфигурация.
Самих конфигураций и альтернативных установок у каждой из этих конфигураций для одного логического устройства может существовать несколько, но в каждый момент времени только один из этих наборов может быть активен. Причём хост должен знать, какой именно набор активен и в соответствии с этим обеспечивать связь с входящими в этот набор конечными точками. Остальные конечные точки, не входящие в активный набор, не будут доступны для связи.
На этом, пожалуй, с основами закончим и в следующей статье попробуем более детально рассмотреть механизм передачи данных по интерфейсу USB.
Запросы, пакеты и транзакции
Для передачи или приема данных клиентское ПО посылает к каналу пакет запроса ввода/вывода — IRP (Input/Output Request Packet) и ждет уведомления о завершении его отработки. Формат IRP определяется реализацией драйвера USBD в конкретной ОС. В IRP имеются только сведения о запросе (местоположение буфера передаваемых данных в оперативной памяти и длина передачи); от свойств конкретного текущего подключения (скорость, допустимый размер пакета) драйвер устройства абстрагируется. Отработкой запроса в виде транзакций на шине USB занимается драйвер USBD; при необходимости он разбивает на части длинные запросы (пакеты), пригодные для передачи за одну транзакцию. Транзакция на шине USB — это последовательность обмена пакетами между хостом и ПУ, в ходе которой может быть передан или принят один пакет данных (возможны транзакции, в которых данные не передаются). Отработка запроса считается завершенной, когда успешно выполняются все связанные с ним транзакции. «Временные трудности», встречающиеся при их выполнении (неготовность к обмену данными), до сведения клиентского драйвера не доводятся — ему остается только ждать завершения обменов (или выхода по тайм-ауту). Однако устройство может сигнализировать о серьезных ошибках (ответом STALL), что приводит к аварийному завершению запроса, о чем уведомляется клиентский драйвер. В этом случае отбрасываются и все последующие запросы к данному каналу. Возобновление работы с данным каналом возможно лишь после явного уведомления об обработке ошибочной ситуации, которое драйвер устройства делает с помощью специального запроса (тоже вызова USBD).
Длинные запросы разбиваются на транзакции так, чтобы использовать максимальный размер пакета. Последний пакет с остатком может оказаться короче максимального размера. Хост-контроллер имеет средства обнаружения приема от устройства «неполновесного» пакета, размер которого меньше ожидаемого. В запросе IRP указывается, следует ли особым образом реагировать на это событие. Особая реакция может быть двоякой:
- считать короткий пакет разделителем, указывающим на конец блока данных. При этом данный IRP завершается нормально и исполняются следующие запросы к данному каналу;
- считать короткий пакет признаком ошибки, по которому канал останавливается (все его последующие ожидающие запросы сбрасываются).
При передаче массивов использование укороченных пакетов в качестве разделителей наиболее естественно. Таким образом, например, в одном из вариантов протоколов для устройств хранения данных укороченные пакеты известной длины используются в качестве управляющих.
Запросы, пакеты и транзакции
Читайте также: