Что такое дескриптор usb
Вы здесь: Главная USB Устройства USB Дескрипторы
Архитектура ЭВМ
Компоненты ПК
Интерфейсы
Мини блог
Самое читаемое
- Арифметико логическое устройство (АЛУ)
- Страничный механизм в процессорах 386+. Механизм трансляции страниц
- Организация разделов на диске
- Диск Picture CD
- White Book/Super Video CD
- Прямой доступ к памяти, эмуляция ISA DMA (PC/PCI, DDMA)
- Карты PCMCIA: интерфейсы PC Card, CardBus
- Таблица дескрипторов прерываний
- Разъемы процессоров
- Интерфейс Slot A
Дескрипторы
В USB принята иерархия дескрипторов, описывающих все свойства устройств. Стандартные дескрипторы USB начинаются с байта длины дескриптора, за которым следует байт, определяющий тип дескриптора.
- дескриптор устройства, Device Descriptor (тип 1), описывающий устройство в целом (версия USB, класс, производитель, модель, протокол, число возможных конфигураций). Для HS-устройств общее описание дополняется дескриптором-квалификатором, Device Qualfier Descriptor (тип 6), в котором указывается, сколько у устройства будет конфигураций при работе на иной скорости (дескриптор устройства относится к той скорости, на которой устройство работает в данный момент);
- дескрипторы конфигурации, Configuration Descriptor, (тип 2), описывающие число интерфейсов, атрибуты (способ питания и возможность генерации удаленного пробуждения) и мощность, потребляемую от шины в каждой конфигурации для текущей скорости. Для HS-устройств имеются и дескрипторы конфигураций для иной скорости, Other Speed Configuration Descriptor (тип 7), описывающие те же параметры с тем же форматом;
- дескрипторы интерфейсов, Interface Descriptor (тип 4), для каждого из интерфейсов, доступных в указанной конфигурации, сообщает число прикладных конечных точек, класс и протокол интерфейса. В конфигурации может быть несколько альтернативных вариантов (alternate settings) данного интерфейса, каждому из которых соответствует свой дескриптор интерфейса. Первичным интерфейсом (primary interface) называют нулевой альтернативный вариант (alternate settings = 0);
- дескрипторы конечных точек, Endpoint Descriptor (тип 5) определяют номер и направление точки, атрибуты (тип передач), максимальный размер поля данных и интервал обслуживания (для точек периодических передач);
- строковые дескрипторы, String Descriptor (тип 3) — необязательные текстовые строки информации, которые могут отображаться хостом. Ссылки (однобайтные индексы) на строковые дескрипторы имеются в дескрипторах устройства, конфигураций и интерфейсов. Нулевое значение индекса означает отсутствие строкового дескриптора для данной структуры. Строки состоят из 2-байтных символов UNICODE, явного терминатора строки нет — конец определяется по длине, заявленной в заголовке дескриптора. Строковые дескрипторы могут присутствовать в устройствах на разных языках; для выбора нужного дескриптора в запросе кроме индекса указывается и 16-битный идентификатор языка (LanguageID). Строковый дескриптор, вызываемый по нулевому индексу (с любым кодом языка), в своем теле содержит список поддерживаемых идентификаторов языка. Значения LanguageID для некоторых языков:
— русский — 0419h;
— английский (США) — 0409h;
— английский (Великобритания) — 0809h;
— белорусский — 0423h;
— украинский — 0422h; - специфические дескрипторы, Class-Specific Descriptor, могут использоваться в устройствах определенных классов. Так, например, для хабов имеется дескриптор интерфейса питания, Interface Power (тип 8). Специфические дескрипторы также должны начинаться с поля длины и типа.
Дескрипторы от устройств хост получает по запросам Get_Descriptor, указав тип дескриптора. Таким способом можно явно запросить дескриптор устройства (и квалификатор), дескриптор конфигурации (для текущей и иной скорости) и дескриптор строки (а также дескриптор OTG). Дескрипторы интерфейсов и конечных точек по отдельности не адресуются, они пристраиваются «хвостом» к дескриптору конфигурации. Возможность чтения всех имеющихся дескрипторов обязательна для всех устройств, дополнительно может поддерживаться и запись дескрипторов (запросом Set_Descriptor). Положительный ответ устройства на транзакцию записи дескриптора означает, что он принят и устройство будет подчиниться его свойствам.
По запросу дескриптора конфигурации устройство выдает целую цепочку дескрипторов, начинающуюся с собственно дескриптора конфигурации. За ней для каждого доступного интерфейса следует дескриптор первичного интерфейса и его конечных точек, за которым следуют все альтернативные варианты со своими конечными точками. Общая длина всего описания конфигурации заранее не известна, она указывается в поле wTotalLength дескриптора конфигурации. Так что для получения конфигурации сначала выполняют запрос, указав длину собственно дескриптора конфигурации (9, хотя достаточно и 4), а потом его повторяют с длиной, прочитанной из данного поля.
В приведенных ниже таблицах и описаниях перед мнемоническими названиями полей префикс b означает байт, w — двухбайтное слово, bm — битовую карту, i — целое число (однобайтный индекс), bcd — неупакованный BCD-формат, id — идентификатор (не число). Отметим, что в пакетах слово передается младшим байтом вперед, так что обращение к слову, размещенному в памяти хоста, будет естественным для процессоров x86 (младший байт по меньшему адресу) и не требует перестановки байтов.
Дело в том, что для каждого класса устройств написаны свои спецификации, которые позволяют унифицировать общение с устройствами. Это удобно и разработчикам устройств (не надо каждый раз изобретать новые интерфейсы и протоколы) и производителям операционок (можно организовать поддержку всяких стандартных устройств на уровне операционок). Да и для простых пользователей навигация в менеджере устройств становится приятнее.
Наряду с классом, определить для чего предназначен девайс, как с ним общаться и какие дрова при этом использовать, помогает пара идентификаторов VID/PID, а также номер версии устройства.
Дескрипторы бывают стандартные и специфические. Стандартные дескрипторы для всех USB-устойств одинаковы, они обязательно есть в любом USB-устройстве и содержат общее описание устройства и его отдельных частей. Специфические дескрипторы содержат всякую специфическую для различных классов устройств информацию, соответственно, такие дескрипторы для каждого класса устройств свои.
Ниже описаны структуры и назначения различных стандартных дескрипторов:
Номер версии USB может принимать следующие значения:
Коды классов бывают следующими:
Битовая маска атрибутов содержит следующую информацию:
Битовая маска атрибутов содержит следующую информацию:
Дескрипторы строк являются необязательными. Если девайс не поддерживает дескрипторы строк, то во всех остальных дескрипторах индексы дескрипторов строк, содержащих текстовое описание, должны быть равны нулю.
При подключении USB-устройства, хост с помощью управляющих передач запрашивает у устройства список дескрипторов в следующем порядке:
- стандартный дескриптор устройства
- дескрипторы конфигураций
Причём при запросе дескриптора конфигурации хост получает сразу все дескрипторы (дескрипторы интерфейсов, конечных точек), относящиеся к этой конфигурации. Общий объём этих дескрипторов хосту заранее неизвестен, он хранится в поле wTotalLength дескриптора конфигурации, поэтому хост сначала запрашивает первые 8 байт этого дескриптора, запоминает из прочитанных данных значение поля wTotalLength, а потом снова запрашивает этот же дескриптор конфигурации, но размер запрашиваемых данных уже указывает равным wTotalLength. В результате такого трюка устройство присылает хосту все дескрипторы, относящиеся к данной конфигурации, в следующем виде:
Специфические дескрипторы мы рассмотрим подробнее когда будем разрабатывать какой-нибудь конкретный девайс, какого-нибудь определённого класса, а пока на этом всё.
Известно, что к проектированию архитектуры шины USB подходили весьма основательно. Хотели создать архитектуру, которая сможет удовлетворить потребности самых сложных периферийных устройств, широкого спектра применения. Для этого придумали достаточно сложную структуру иерархических данных, называемую набор дескрипторов. В этой структуре, формат которой строго регламентируется стандартом, должна храниться вся информация, с помощью которой хост может автоматически конфигурироваться и начинать нормальную работу с USB устройством. Именно набором дескрипторов USB устройства обменивается с хостом во время процесса энумерации. Имеется 4 основных типа дескрипторов: дескриптор устройства, дескриптор конфигурации, дескриптор интерфейса и дескриптор конечной точки. Все эти виды дескрипторов должны обязательно присутствовать в USB устройстве. Рисунок 3 поясняет сказанное.
Конечные точки объединяются в интерфейс. Каждому интерфейсу соответствует драйвер операционной системы (ОС) хоста. Некоторые устройства могут иметь несколько интерфейсов, которые могут функционировать одновременно. Телефонный аппарат VOIP может иметь интерфейс клавиатуры и аудио интерфейс, тогда ОС хоста будет использовать 2 различных драйвера, для каждого интерфейса. А прикладная программа, будет взаимодействовать с этими драйверами для функционирования. Несколько интерфейсов, могут объединяться в конфигурацию. У USB устройства может быть несколько конфигураций, но не может быть активными несколько конфигураций одновременно, возможно переключение между конфигурациями. В наших примерах не будем использовать USB устройства с несколькими дескрипторами конфигураций и несколькими дескрипторами интерфейсов.
Кроме базовых дескрипторов, которые являются основными, для создания работоспособного устройства, имеются и прочие дескрипторы. Об этих дескрипторах упомянем после.
С начала для сторонников Linux, потом для Windows. Что представляют дескрипторы мыши USB, можно увидеть, воспользовавшись утилитой lsusb для Linux. Эта команда не в точности выда.т структуру дескрипторов USB устройства, но очень близкую. Введ.м команду и перенаправим е. вывод в текстовый файл, а затем файл откроем в любом текстовом редакторе.
Здесь в качестве параметра указан идентификатор устройства, дескрипторы которого требуется вывести и дополнительные ключи. Ищем и открываем файл dsc.txt. Как узнать идентификатор мыши PC, писалось выше.
Посмотрим на содержимое файла dsc.txt. Легко распознать секции с дескрипторами устройства, конфигурации, интерфейса и конечной точки. В названиях полей дескрипторов имеются префиксы, соответственные типу данных.
Начнем с описания полей дескриптора устройства.
Поле | Размер | Описание |
bLength | 1 | размер дескриптора в байтах |
bDescriptorType | 1 | тип дескриптора |
bcdUSB | 2 | номер спецификации USB, которой удовлетворяет устройство, в двоично-десятичном формате |
bMaxPacketSize | 1 | максимальный размер пакета для конечной точки 0, из ряда 8,16,32,64 |
idVendor | 2 | код разработчика |
idProduct | 2 | код разработки |
bcdDevice | 2 | код версии разработки, в двоично-десятичном виде |
iManufacturer | 1 | индекс текстовой строки производителя |
iProduct | 1 | индекс текстовой строки изделия |
iSerial | 1 | индекс текстовой строки серийного номера |
bNumConfigurations | 1 | количество возможных конфигураций |
Поле bMaxPacketSize зада.т размер максимального пакета конечной точки управления. Конечная точка управления используется для приема команд от хоста. Эта конечная точка не имеет дескриптора, только задается размер ее буфера. Дескрипторы могут содержать не только информацию в двоичном коде, а так же и текст. Эта информация не обязательна для работы USB устройства, но весьма ценна для человеческого восприятия. Для передачи этой информации служат строковые дескрипторы. Дескриптор строки состоит из 2 байтных символов UNICODE-16 v1. Такая кодировка используется в Windows. Структура строковых дескрипторов не описана в дампе вывода, lsusb извлекла текст по умолчанию и подставила в отчет. Поля с индексами, указывают на номер строкового дескриптора в пуле строковых дескрипторов. Если в индексном поле задан 0, то считается, что строковый дескриптор не задан. Из дампа информации выведенной lsusb видно, что iSerial не задано, а описатель iProduct имеет индекс 2.
Далее идет дескриптор конфигурации.
Поле | Размер | Описание |
wTotalLength | 1 | полная длина возвращаемых данных |
bNumInterfaces | 1 | количество интерфейсов |
bConfigurationValue | 1 | условный номер конфигурации |
iConfiguration | 1 | индекс строкового дескриптора к конфигурации |
bmAttributes | 1 | атрибуты конфигурации |
bMaxPower | 1 |
Поле bMaxPower задает максимальный ток, потребляемый USB устройством. Задается в единицах кратных 2 миллиамперам. В дампе lsusb этот параметр уже перевед.н в миллиамперы. Поле bmAttributes – набор битов, способ одним байтом задать несколько параметров. Этим полем задаются энергетические характеристики устройства. Поэтому сделаем некоторое введение.
Все USB устройства делятся на 3 класса по потребляемой энергии: питаемые от шины, питаемые от шины с высоким потреблением энергии и имеющие собственный источник энергии. Напомним, что USB устройства подключаемые к шине USB, могут получать энергию по самой шине. Те что потребляют меньше 100 миллиампер, относятся к классу с низким потреблением (Low power ). А USB устройства потребляющие до 500 миллиампер от самой шины, относятся к мощным (High power). Третья категория это те, что имеют собственный источник (Self powered). Имеющие собственный источник питания, могут потреблять дополнительно и от шины, но не боле 100 миллиампер.
Если установлен бит 6 поля bmAttribute, то это указывает, что USB устройство имеет собственный источник питания. Бит 5, что USB устройство может являться источником сигнала пробуждения, выводящим вышестоящий хаб из состояния низкого потребления. Теперь ясно, почему моя мышь может разбудить компьютер. Остальные биты зарезервированы.
В дескрипторе интерфейса, пока только интересным являются поля bNumEndpoints и поле iInterface. Из названия ясно, что одно поле зада.т количество конечных точек в интерфейсе, не считая управляющей конечной точки. Другое поле - индекс строкового дескриптора, указывающий на строковый дескриптор интерфейса. Поля относящиеся к секции HID Device Descriptor, рассматривать не будем.
Последним идет дескриптор единственной конечной точки.
Поле | Размер | Описание |
bEndpointAddress | 1 | адрес конечной точки |
bmAttributes | 1 | битовое поле атрибутов |
wMaxPacketSize | 1 | максимальный размер пакета |
bInterval | 1 | интервал опроса в миллисекундах |
Адрес конечной точки состоит из 2-х частей, номер и направление. Для номера выделены биты 0..3 , а для направления предназначен бит 7. Если этот бит установлен, то конечная точка ввода. Остальные биты зарезервированы. Всего можно адресовать до 30 точек, 15 на ввод и 15 на вывод. Нулевой адрес зарезервирован для точки управления. Из дампа вывода видно, что у мыши конечная точка типа ввода, посылает данные на хост и имеет номер 1.
Поле bmAttributes , биты 0..1 задают тип точки: 0 – управления, 1 – изохронной передачи, 2 – массовой передачи, 3 – передачи по прерыванию. Биты 2..5 имеют смысл только для изохронных типов конечных точек. Биты 2..3 задают тип синхронизации для изохронной конечной точки: 0 – нет синхронизации, 1 – асинхронная, 2 – адаптивная, 3 – синхронная. Биты 4..5 задают тип использования изохронной конечной точки: 0 – для данных , 1 – обратная связь, 2 – данные с предоставлением неявной обратной связи, 3 – зарезервировано. Смысл этих параметров относится к изохронным передачам, которые не будем рассматривать подробно. Поле wMaxPacketSize , зада.т максимальный размер пакета, который способна принять точка. Мышь переда.т хосту пакеты данных по 4 байта. Поле bInterval зада.т максимально возможную задержку опроса, для конечных точек передач по прерыванию. При поступлении запроса от хоста на передачу данных, данные будут переданы с возможной задержкой не более чем значение в bInterval. Для изохронных точек всегда 1. Для остальных не имеет значения. Мышь опрашивается с частотой 10 миллисекунд.
В ОС Windows дамп вывода программы UsbTreeView для мыши USB, приведен ниже, нет только информации специфичной для Windows. По моему мнению, информация более удобная для восприятия. Строковые дескрипторы показываются отдельно.
Здесь все те же дескрипторы, только в ином виде. Без труда можно разобраться, с учетом информации приведенной выше для Linux.
Этот набор команд файла, запустит диспетчер устройств и установит значение переменной среды. Далее установить галочку в меню «Вид -> Показать скрытые устройства ». Записи о драйверах скрытых устройств будут приглуш.нного цвета, удалите ненужную запись и вставьте штекер USB проблемного устройства. Проблемное устройство будет сконфигурировано с белого листа. Те же задачи, но более комфортно можно решить с программой USBDeview от Nir Sofer.
Известно, что к проектированию архитектуры шины USB подходили весьма основательно. Хотели создать архитектуру, которая сможет удовлетворить потребности самых сложных периферийных устройств широкого спектра применения. Для этого придумали достаточно сложную структуру иерархических данных, называемую набор дескрипторов. В этой структуре, формат которой строго регламентируется стандартом, должна храниться вся информация, с помощью которой хост может автоматически конфигурироваться и начинать нормальную работу с USB-устройством. Именно набором дескрипторов USB-устройство обменивается с хостом во время процесса энумерации. Имеется 4 основных типа дескрипторов:
- дескриптор устройства,
- дескриптор конфигурации,
- дескриптор интерфейса,
- дескриптор конечной точки.
Все эти виды дескрипторов должны обязательно присутствовать в USB-устройстве. Рисунок 2 поясняет сказанное.
Рисунок 2. |
Конечные точки объединяются в интерфейс. Каждому интерфейсу соответствует драйвер операционной системы (ОС) хоста. Некоторые устройства могут иметь несколько интерфейсов, которые могут функционировать одновременно. Телефонный аппарат VOIP может иметь интерфейс клавиатуры и аудио интерфейс, тогда ОС хоста будет использовать 2 различных драйвера для каждого интерфейса. А прикладная программа будет взаимодействовать с этими драйверами для функционирования. Несколько интерфейсов могут объединяться в конфигурацию. У USB-устройства может быть несколько конфигураций, но не могут быть активными несколько конфигураций одновременно, необходимо переключение между конфигурациями. В наших примерах USB-устройства с несколькими дескрипторами конфигураций и несколькими дескрипторами интерфейсов мы не использовать будем.
Кроме базовых дескрипторов, которые являются основными, для создания работоспособного USB-устройства, имеются и прочие дескрипторы. Об этих дескрипторах расскажем позднее.
Что представляют собой дескрипторы мыши USB, можно увидеть, воспользовавшись утилитой lsusb. Эта команда не в точности выдает структуру дескрипторов USB-устройства, но очень близкую. Введем команду и перенаправим ее вывод в текстовый файл, а затем файл откроем в любом текстовом редакторе
Здесь в качестве параметра указан идентификатор устройства, дескрипторы которого требуется вывести, а также дополнительные ключи. Ищем и открываем файл dsc.txt. Как узнать идентификатор мыши PC, говорилось выше.
Посмотрим на содержимое файла dsc.txt. Легко распознать секции с дескрипторами устройства, конфигурации, интерфейса и конечной точки. В названиях полей дескрипторов имеются префиксы, соответствующие типам данных:
Начнем с описания полей дескриптора устройства:
номер спецификации USB, которой удовлетворяет устройство (в двоично-десятичном формате) максимальный размер пакета для конечной точки 0, из ряда 8, 16, 32, 64Поле bMaxPacketSize задает размер максимального пакета конечной точки управления. Конечная точка управления используется для приема команд от хоста. Эта точка не имеет дескриптора, задается только размер ее буфера. Дескрипторы могут содержать не только информацию в двоичном коде, а так же и текст. Эта информация необязательна для работы USB-устройства, но весьма ценна для человеческого восприятия. Для передачи этой информации служат строковые дескрипторы. Дескриптор строки состоит из 2-байтных символов UNICODE-16 v1. Такая же кодировка используется в Windows. Структура строковых дескрипторов в дампе вывода не описана, lsusb извлекла текст по умолчанию и подставила в нужные места отчета. Поля с индексами указывают на номер строкового дескриптора в пуле строковых дескрипторов. Если в индексном поле задан 0, то считается, что строковый дескриптор не задан. Из дампа информации, выведенной lsusb, видно, что iSerial не задано, а описатель iProduct имеет индекс 2.
Далее идет дескриптор конфигурации:
Поле bMaxPower задает максимальный ток, потребляемый USB-устройством. Задается в единицах, кратных 2 мА. В дампе lsusb этот параметр уже переведен в миллиамперы. Поле bmAttributes – набор битов, способ одним байтом задать несколько параметров. Этим полем задаются энергетические характеристики устройства. Поэтому сделаем некоторое введение.
Все USB-устройства делятся на 3 класса по потребляемой энергии: питаемые от шины, питаемые от шины с высоким потреблением энергии и имеющие собственный источник энергии. Напомним, что USB-устройства, подключаемые к шине USB, могут получать энергию по самой шине. Те что потребляют меньше 100 миллиампер, относятся к классу с низким потреблением (Low power ). А USB-устройства потребляющие до 500 миллиампер от самой шины, относятся к мощным (High power). Третья категория – это те, что имеют собственный источник (Self powered). Имеющие собственный источник питания могут потреблять дополнительно и от шины, но не боле 100 миллиампер.
Если установлен бит 6 поля bmAttribute, то это указывает, что USB-устройство имеет собственный источник питания. Бит 5 – что USB-устройство может являться источником сигнала пробуждения, выводящим вышестоящий хаб из состояния низкого потребления. Теперь ясно, почему моя мышь может разбудить компьютер. Остальные биты зарезервированы.
В дескрипторе интерфейса интересными пока являются только поля bNumEndpoints и поле iInterface. Из названия ясно, что одно поле задает количество конечных точек в интерфейсе, не считая управляющей конечной точки. Другое поле – индекс строкового дескриптора, поясняющий интерфейс. Поля, относящиеся к секции HID Device Descriptor, рассматривать не будем.
Последним идет дескриптор единственной конечной точки:
Адрес конечной точки состоит из 2-х частей: номера и направления. Для номера выделены биты 0..3, а для направления предназначен бит 7. Если этот бит установлен, то конечной будет точка ввода. Остальные биты зарезервированы. Всего можно адресовать до 30 точек – 15 на ввод и 15 на вывод. Нулевой адрес зарезервирован для точки управления. Для мыши видно, что конечная точка типа ввода посылает данные на хост и имеет номер 1.
Поле bmAttributes, биты 0..1 задают тип точки: 0 – управления, 1 – изохронной передачи, 2 – массовой передачи, 3 – передачи по прерыванию. Биты 2..5 имеют смысл только для изохронных типов конечных точек. Биты 2..3 задают тип синхронизации для изохронной конечной точки: 0 – нет синхронизации, 1 – асинхронная, 2 – адаптивная, 3 – синхронная. Биты 4..5 задают способ использования изохронной конечной точки: 0 – для данных, 1 – обратная связь, 2 – данные с предоставлением неявной обратной связи, 3 – зарезервировано. Смысл этих параметров относится к изохронным передачам, которые не будем рассматривать подробно. Поле wMaxPacketSize задает максимальный размер пакета, который способна принять точка. Мышь передает хосту пакеты данных по 4 байта. Поле bInterval задает максимально возможную задержку опроса для конечных точек передач по прерыванию. При поступлении запроса от хоста на передачу данных данные будут переданы с возможной задержкой не более, чем значение в bInterval. Для изохронных точек всегда 1. Для остальных не имеет значения. Мышь опрашивается с периодичностью 10 мс.
Первая программа
Напишем приложение, работающее с USB-устройством. В качестве устройства будем использовать мышь USB. Программа написана на GCС в Linux. Для навигации по исходному коду программы и библиотек лучше использовать какую-нибудь IDE для C.
Проблема написания приложения для устройства USB состоит в необходимости создания драйвера. Создание драйвера – задача не рядовая. Получается, что для изучения USB требуется предварительно научиться писать драйверы для ОС.
Другой подход – это использовать имеющиеся драйверы ОС. Уже говорилось о USB-устройствах, имеющих архитектуру, относящуюся к определенным стандартизованным классам. Для таких типов устройств уже имеются драйверы. Например, мышь относится к классу HID, и для нее имеются стандартные драйверы в ОС. Такой подход имеет право на жизнь. Через API из своей программы обращаетесь к ОС, а она через драйвер к USB-устройству. Но тогда созданное USB-устройство должно поддерживать протокол HID. Это достаточно сложная надстройка над базовой функциональностью USB.
Оба этих метода не подходят для начинающих изучать USB. Так как быть? К счастью для новичков, имеется библиотека libusb. Первоначально она создавалась для Linux, теперь имеются порты и для Windows. Уже известная утилита lsusb использует библиотеку libusb. Эта библиотека позволяет писать прикладные программы, напрямую обращающиеся к USB-устройству, устраняя необходимость использования драйверов ОС.
В Ubuntu для программирования требуется установить пакет libusb-dev, или установить библиотеку из исходных кодов. Для понимания работы с библиотекой необходимо знание работы со структурами и со списками из структур на языке C. Для компилирования так же необходимо иметь на машине компилятор GCC. На моем компьютере была установлена версия библиотеки libusb 0.1.12.
Итак, нам осталось разобраться с процессом обработки стандартных запросов USB и с дескрипторами. Давайте сначала разберемся с теорией, а потом подробно разберем пример обработки конкретного запроса.
Обработка запросов.
Процесс обработки запроса состоит из трех фаз (SETUP, DATA, STATUS), причем фаза DATA является опциональной.
Как мы помним из предыдущей статьи, для передачи запросов хост использует специальный TOKEN пакет — TOKEN SETUP. Собственно говоря, первая фаза состоит из того, что хост посылает TOKEN SETUP, затем посылает пакет данных (8 байт) который содержит запрос. Устройство подтверждает получение запроса ACK пакетом. Структуру запроса мы рассмотрим ниже, пока перейдем к следующей фазе – фазе обмена данными.
Дело в том, что некоторые типы запросов предполагают передачу дополнительных данных, как от хоста к устройству (передача дополнительных параметров запроса) так и от устройства к хосту (получение хостом ответа на запрос). Этот обмен и составляет фазу обмена данными.
Хост, отравив запрос в SETUP фазе, знает тип запроса и, соответственно, знает: нужна ли для данного типа запроса фаза обмена данными и в какую сторону будет происходить обмен.
Если запрос предполагает передачу дополнительных параметров — хост отправляет TOKEN OUT и пакет данных содержащий дополнительные параметры запроса. Устройство подтверждает получение пакета данных, после чего хост может послать следующий пакет данных и т. д.
Соответственно, если запрос предполагает получение ответа от устройства, хост посылает TOKEN IN и ожидает пакет данных от устройства.
Последняя (STATUS) фаза нужна для подтверждения корректности обработки всего запроса. На данной фазе устройство или хост просто посылает пакет данных нулевой длины, что свидетельствует об успешной обработке запроса на логическом уровне. Если запрос предполагал получение ответа от устройства – то данный пакет посылает хост, подтверждая, что ответ получен/обработан. В другом случае – пакет нулевой длины шлет устройство, подтверждая, что запрос (запрос и дополнительные параметры) получены и обработаны.
Если что-то пошло не так (например, мы получили запрос который не умеем обрабатывать), устройство может послать STALL пакет, что свидетельствует об ошибке на логическом уровне. Но хост расценит это как фатальную ошибку т. к. стандартные запросы должны всегда корректно обрабатывается всеми USB устройствами.
Теперь заглянем «внутрь» запроса (8 байт данных полученных устройством в SETUP фазе). Запрос состоит из следующих полей:
bmRequestType — 1 байт
bRequest – 1 байт
wValue – 2 байта, WORD
wIndex – 2 байта, WORD
wLength – 2 байта, WORD
bmRequestType – битовое поле, которое содержит характеристики запроса:
Бит 7: Направление передачи данных в DATA фазе:
0 = от хоста к устройству
1 = от устройства к хосту
Биты 6. 5: Тип запроса
0 = стандартный запрос USB
1 = стандартный запрос для определенного класса устройств USB
2 = пользовательский запрос
3 = зарезервировано
Биты 4. 0: Кому адресован запрос
0 = устройству
1 = интерфейсу
2 = конечной точке
3 = другое
4. 31 = зарезервировано
bRequest – уникальный код запроса (например, 0 = GET_STATUS, 5 = SET_ADDRESS, 6 = GET_DESCRIPTOR и т. д.)
Здесь следует пояснить, зачем нужны биты 4..0 поля bmRequestType. Например, для получения статуса хост посылает запрос GET_STATUS (bRequest = 0). Значение бит 4. 0 поля bmRequestType определяет, статус чего именно хочет получить хост: 0 – статус устройства, 1 – статус интерфейса, 2 – статус контрольной точки и т. д.). Таким образом, «тип запроса» определяется битами 4..0 поля bmRequestType и полем bRequest.
wValue – данное поле может использоваться для передачи параметров запроса. Например, для запроса SET_ADDRESS данное поле содержит адрес, который нужно присвоить устройству. Если параметры запроса не помещаются в 2 байта wValue, то хост их передает в DATA фазе.
wIndex – значение данного поля зависит от типа запроса. Например, для запроса GET_STATUS к конечной точке, данное поле содержит индекс конечной точки, статус которой хост хочет получить.
wLength – данное поле содержит размер данных, которые будут переданы хостом в DATA фазе либо которые хост ожидает получить в DATA фазе.
Для примера, рассмотрим алгоритм обработки запроса GET_DESCRIPTOR.
1. Хост посылает устройству запрос:
bmRequestType = 0x80
bRequest = 6 (GET_DESCRIPTOR)
wValue = 0x100 (тип дескриптора, который хост хочет получить, в данном случае дескриптор устройства)
wIndex = 0 (в данном случае параметр не используется)
wLength = 18 (в ответ хост хочет получить 18 байт)
Из запроса видно, что хост хочет получить дескриптор нашего устройства и что за SETUP фазой предполагается DATA фаза «от устройства к хосту».
2. Устройство посылает хосту 18 байт дескриптора (DATA фаза).
3. Хост подтверждает успешное завершение обработки запроса, отправив устройству пакет данных нулевой длины (STATUS фаза).
Будем считать, что с обработкой запросов мы разобрались. Все запросы подробно разбирать мы не будем, т. к. это очень объемный материал. Детально все типы стандартных запросов описаны в разделе 9.3 официальной спецификации.
Касательно нашего CDC устройства — помимо стандартных запросов USB, для нашего класса устройств предусмотрено несколько дополнительных запросов (SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE). Эти запросы предназначены для установки параметров (Baudrate, Stop Bits, Parity, Data bits) нашего виртуального COM-порта. В нашей реализации эти параметры нам не нужны, но обрабатывать эти запросы мы обязаны, поэтому реализуем обработку в виде «заглушек».
Дескрипторы
Теперь поговорим о дескрипторах.
Стандартом определены следующие типы дескрипторов:
— Дескрипторы устройства
— Дескрипторы конфигурации
— Дескрипторы интерфейса
— Дескрипторы конечной точки
— Строковые дескрипторы
Дескриптор устройства всегда один, он содержит базовую информацию об устройстве (код производителя, код устройства, класс устройства и т. д.). Мы позже детально разберем данный дескриптор на примере нашего устройства.
Дескрипторы конфигурации описывают конфигурации нашего устройства.
Здесь остановимся немного подробнее. Дело в том, что устройство может поддерживать несколько альтернативных конфигураций. Например, в конфигурации «1» устройство питается от интерфейса USB, а в конфигурации «2» – от внешнего источника. Или в конфигурации «1» устройство для обмена данными с хостом использует дополнительные контрольные точки типа bulk, а в конфигурации «2» – весь обмен идет через «нулевую конечную точку».
Для каждой из конфигураций устройство хранит свой дескриптор конфигурации. Хост получает по запросу дескрипторы всех конфигураций и выбирает одну из возможных конфигураций (логика выбора «рабочей» конфигурации из возможных заложена в драйвере устройства). После этого, хост назначает устройству номер текущей конфигурации (отправив стандартный запрос SET_CONFIGURATION) и дальнейшая работа устройства происходит в соответствии с выбранной хостом конфигурацией.
Для каждой конфигурации устройства определен один или несколько «интерфейсов». Интерфейс определяет, какие контрольные точки будут использоваться для обмена данными между хостом и устройством. Эта информация и есть «дескриптор интерфейса».
Информация о каждой конечной точке интерфейса называется «дескриптор конечной точки». Для каждой конечной точки дескриптор описывает тип конечной точки, ее параметры (такие как максимальный размер буфера обмена и т. д.)
Каждый дескриптор конфигурации содержит в себе дескрипторы интерфейса, а каждый дескриптор интерфейса содержит дескрипторы конечных точек.
Строковые дескрипторы опциональны, они позволяют получить хосту, в виде UNICODE строки, название устройства, название производителя устройства, серийный номер устройства.
Для примера, рассмотрим «дескриптор устройства», пример которого уже приводился в одной из статей.
Итак, в дескрипторе содержится следующая ключевая информация:
1. Устройство поддерживает версию стандарта 2.0
2. Устройство относится к классу CDC
3. Код производителя 0x03EB (Atmel)
4. Код продукта (в рамках производителя) 0x6127
5. Код ревизии устройства 0x0110
6. Устройство поддерживает одну конфигурацию
Компания Atmel позволяет использовать свой код производителя (VID) при создании USB устройств на базе ее продукции, правда с небольшими оговорками. Остальные параметры (Код продукта, Код ревизии устройства) выбраны «с потолка» :)
Комбинация VID/PID используется ОС для идентификации устройства и поиска «подходящего» драйвера. В некоторых случаях, для поиска драйвера используется информация о классе устройства (например, для HID и MSD устройств).
Описывать «побитно» все дескрипторы мы не будем, эта информация подробно описана в разделе 9.6 спецификации.
Теперь фрагмент кода — реализации данного уровня логики USB.
Эта статья завершает цикл.
Как и обещал – выкладываю исходные коды примера реализации (не стоит забывать, что это лишь пример, реализация очень упрощена). Для демонстрации работы стека в main.c реализована простая эхо-отвечалка, все данные полученные от ПК устройство отправляет обратно :)
Читайте также: