Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Интерфейс USB. Часть 3. Что должно уметь любое USB-устройство. Состояния, управляющие передачи, стандартные запросы

Итак, начнём мы с того, что спецификацией определены для 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 состоит из пустой транзакции ввода или вывода и служит для определения успешности или неуспешности завершения управляющей передачи.

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

структура управляющей передачи USB

Пакет данных транзакции управления всегда должен иметь PID Data0. Далее идентификаторы Data1, Data0 чередуются, но пакет данных передаваемый на стадии Status Stage должен иметь идентификатор Data1, независимо от того, какой идентификатор использовался перед этим.

На стадии Status Stage устройство при успешном выполнении запроса отвечает пустым пакетом Data1 на транзакцию IN или пакетом Ack на транзакцию OUT. Если оно ещё не завершило выполнение запроса, то оно отвечает пакетом NAK. Если при выполнении запроса произошла ошибка, то устройство отвечает пакетом STALL (для управляющих конечных точек такая ситуация не требует вмешательства хоста и не вызывает блокировки канала).

Далее давайте рассмотрим более подробно из каких полей состоит пакет данных, передаваемый на стадии Setup Stage:

Смещение
(байт)
Название поля Размер
(байт)
Описание
0 bmRequestType 1 Стандартные характеристики запроса
1 bRequest 1 Код запроса (команда)
2 wValue 2 Параметр запроса
4 wIndex 2 Индекс или смещение
6 wLength 2 число байт для передачи на стадии Data Stage

Байт стандартных характеристик содержит следующую информацию:

  1. старший бит показывает направление передачи данных (0 — от хоста, 1 — от устройства)
  2. биты 6,5 определяют тип запроса (00 — стандартный, 01 — для класса, 10 — специфичный, 11 — зарезервировано)
  3. младшие 5 бит определяют получателя (00000 — устройство целиком, 00001 — интерфейс, 00010 — конечная точка, 00011 — другой, остальные значения зарезервированы)

С размером и форматом полей будет легче ориентироваться, если вы запомните маленькую хитрость, придуманную программистами для облегчения своего труда. Первые буквы названия полей (до заглавной) означают следующее:
bm — сокращение от bit mask, такие первые буквы названия означают, что значение имеют отдельные биты поля, то есть поле рассматривается побитово;
b — сокращение от byte, такое поле рассматривается целиком и имеет размер 1 байт
w — сокращение от word, такое поле также рассматривается целиком и имеет размер 2 байта

В дальнейшем я буду битовые поля писать в формате 0bBBBBBBBB, поля размером 1 байт — в формате 0xHH, а двухбайтовые поля — в формате 0xHHHH

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

Название код запроса (bRequest) Назначение
Get_Status 0x00 запрос состояния

Этот запрос используется для получения состояния устройства USB, интерфейса или конечной точки. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b10000000 — получить состояние USB-устройства
  3. 0b10000001 — получить состояние интерфейса
  4. 0b10000010 — получить состояние конечной точки
  5. Как видите, для всех трёх вариантов старший бит равен 1, то есть на стадии передачи данных устройство должно передавать данные хосту (отвечать на этот запрос).
  1. wValue
  2. 0x0000
  1. wIndex
  2. 0x0000 — если запрос обращён к USB-устройству
  3. если запрос обращён к интерфейсу или к конечной точке, то в этом поле указывается номер интерфейса или конечной точки
  1. wLength
  2. 0x0002 — то есть, в ответ на этот запрос, хост, на стадии передачи данных, должен получить от устройства 2 байта информации (слово состояния). Соответственно, если запрос был адресован USB-устройству, то хост получает слово состояния устройства, если запрос был адресован интерфейсу — слово состояния интерфейса, если конечной точке — слово состояния конечной точки. Как видите, всё не так уж и сложно.

Слово состояния устройства имеет следующий формат:

  1. бит 0 — режим питания устройства: 0 — питание от шины, 1 — от внешнего источника
  2. бит 1 — реакция на сигнал пробуждения (remote wakeup): 0 — устройство игнорирует сигнал пробуждения, 1 — устройство реагирует на этот сигнал
  3. биты 2..15 не используются и должны содержать нули

Слово состояния интерфейса всегда должно быть равно нулю (все его биты равны нулю).

Слово состояния конечной точки имеет следующий формат:

  1. бит 0 — признак блокировки конечной точки: 0 — точка не заблокирована, 1 — точка заблокирована
  2. биты 1..15 не используются и должны содержать нули
Название код запроса (bRequest) Назначение
Clear_Feature 0x01 сброс свойства

Этот запрос используется для запрета какого-либо свойства или состояния. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b00000000 — сбросить свойство USB-устройства
  3. 0b00000001 — сбросить свойство интерфейса
  4. 0b00000010 — сбросить свойство конечной точки
  1. wValue
  2. В этом поле содержится код свойства, которое нужно сбросить.
  1. wIndex
  2. 0x0000 — если запрос обращён к USB-устройству
  3. если запрос обращён к интерфейсу или к конечной точке, то в этом поле указывается номер интерфейса или конечной точки
  1. wLength
  2. 0x0000 — то есть, на стадии передачи данных никаких данных передавать не нужно и управляющая передача с таким запросом будет состоять только из двух стадий.
Название код запроса (bRequest) Назначение
Set_Feature 0x03 установка свойства

Этот запрос используется для разрешения какого-либо свойства или состояния. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b00000000 — установить свойство USB-устройства
  3. 0b00000001 — установить свойство интерфейса
  4. 0b00000010 — установить свойство конечной точки
  1. wValue
  2. В этом поле содержится код свойства, которое нужно установить.
  1. wIndex
  2. 0x0000 — если запрос обращён к USB-устройству
  3. если запрос обращён к интерфейсу или к конечной точке, то в этом поле указывается номер интерфейса или конечной точки
  1. wLength
  2. 0x0000
Название код запроса (bRequest) Назначение
Set_Address 0x05 установка адреса

Этот запрос используется для назначения USB-устройству адреса на шине USB. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b00000000 — здесь могут быть только нули, поскольку адрес может быть присвоен только устройству и не может быть присвоен интерфейсу или конечной точке
  1. wValue
  2. В этом поле содержится назначаемый устройству адрес
  1. wIndex
  2. 0x0000
  1. wLength
  2. 0x0000
Название код запроса (bRequest) Назначение
Get_Descriptor 0x06 получить дескриптор

Этот запрос используется для получения дескрипторов. Подробнее о дескрипторах и их форматах мы поговорим позднее, а пока скажу лишь, что это специальные структуры данных, содержащие описание устройства, его интерфейсов, конечных точек и т.д. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b10000000 — получить дескриптор устройства
  3. 0b10000001 — получить дескриптор интерфейса
  4. 0b10000010 — получить дескриптор конечной точки
  1. wValue

  2. В старшем байте этого поля содержится тип дескриптора:
    1. 0x01 — стандартный дескриптор устройства
    2. 0x02 — дескриптор конфигурации
    3. 0x03 — дескриптор строки
    4. 0x04 — дескриптор интерфейса
    5. 0x05 — дескриптор конечной точки
    6. 0x06 — уточняющий дескриптор устройства (для HS-устройств)
    7. 0x07 — дескриптор дополнительной конфигурации
    8. 0x08 — дескриптор управления питанием интерфейса
    9. 0x09 — дескриптор OTG
    10. 0x0A — отладочный дескриптор
    11. 0x0B — дополнительный дескриптор интерфейса
  3. В младшем байте поля wValue содержится индекс дескриптора (если дескриптор относится к устройству, то этот индекс равен нулю).

  1. wIndex
  2. В дескрипторах строк в этом поле содержится идентификатор языка, для остальных типов дескрипторов это поле должно быть равно нулю
  1. wLength
  2. Максимальный размер дескриптора в байтах. Если реальный размер запрашиваемого дескриптора меньше этого значения, то устройство должно просто целиком отослать запрашиваемый дескриптор, а если реальный размер дескриптора больше, то устройство должно послать хосту только первые wLength байт этого дескриптора
Название код запроса (bRequest) Назначение
Set_Descriptor 0x07 записать дескриптор

Этот запрос используется для изменения существующих или добавления новых дескрипторов. Остальные поля могут принимать такие же значения, как и в запросе Get_Descriptor, только старший бит поля bmRequestType в этом случае будет равен нулю, поскольку данные на стадии передачи данных будут передаваться от хоста к устройству.

Название код запроса (bRequest) Назначение
Get_Configuration 0x08 запрос конфигурации

Этот запрос используется для того, чтобы узнать номер активной (выбранной в данный момент) конфигурации. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b10000000
  1. wValue
  2. 0x0000
  1. wIndex
  2. 0x0000
  1. wLength
  2. 0x0001, то есть, на стадии передачи данных, устройство посылает хосту 1 байт данных, содержащий номер активной конфигурации
Название код запроса (bRequest) Назначение
Set_Configuration 0x09 выбор конфигурации

Этот запрос используется для того, чтобы выбрать новую активную конфигурацию для USB-устройства. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b00000000
  1. wValue
  2. 0x00NN, где NN — номер конфигурации, которую надо сделать активной
  1. wIndex
  2. 0x0000
  1. wLength
  2. 0x0000
Название код запроса (bRequest) Назначение
Get_Interface 0x0A запрос номера альтернативной установки

Этот запрос используется для того, чтобы узнать какая альтернативная установка в настоящий момент используется для указанного интерфейса:

  1. bmRequestType
  2. 0b10000001
  1. wValue
  2. 0x0000
  1. wIndex
  2. 0xNNNN, где NNNN — номер интерфейса, для которого мы запрашиваем номер выбранной альтернативной установки
  1. wLength
  2. 0x0001, то есть, на стадии передачи данных устройство посылает хосту 1 байт данных, содержащий номер выбранной в настоящий момент альтернативной установки для указанного в запросе интерфейса
Название код запроса (bRequest) Назначение
Set_Interface 0x0B выбор номера альтернативной установки

Этот запрос используется для того, чтобы выбрать новую альтернативную установку для указанного интерфейса. При этом остальные поля запроса могут принимать следующие значения:

  1. bmRequestType
  2. 0b00000001
  1. wValue
  2. 0x00NN, где NN — номер альтернативной установки, которую мы хотим выбрать
  1. wIndex
  2. 0xNNNN, где NNNN — номер интерфейса, для которого мы выбираем новую альтернативную установку
  1. wLength
  2. 0x0000

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

  1. Часть 1. Основы.
  2. Часть 2. Как происходит передача данных по шине.
  3. Часть 3. Что должно уметь любое USB-устройство.
  4. Часть 4. Дескрипторы и классы.
  5. Часть 5. Программная реализация low speed устройства USB. Схема.
  6. Часть 6. Программная реализация LS устройства USB. Физика и приём пакетов.
  7. Часть 7. Программная реализация LS устройства USB. Разбираем пакеты по типам.
  8. Часть 8. Программная реализация LS устройства USB. Передача по USB произвольного буфера и пакетов подтверждения.
  9. Часть 9. Программная реализация LS устройства USB. Продолжаем разбираться с принятыми пакетами.

Комментарии 1

  • доброго времени суток . статья познавательна в теории и не более потому , что без примера это останется теорией .
    00000001*11000011*00000001011000000000000010000000000000000000000000000010000000001011101100101001
    по вашей статье смог только определить DATA0 выделено *

Добавить комментарий