среда, 7 июля 2021 г.

USB HID дескрипторы и запросы

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

Итак, основные типы:

  • Device - содержит основную информацию об устройстве
  • Configuration - тут уже особенности
  • Interface - у каждого устройства может быть несколько интерфейсов, в простейшем случае он один
  • Endpoint - если совсем грубо то ендпоинт = канал связи, тут он конфигурируется, определяются размеры пакетов и частота их передачи
  • String - так вот откуда берутся все эти надписи в строке авторана, KYE Systems mouse + keyboard + snowboard mega combo device legalize готово к использованию   

const uint8_t gamepadDeviseDesc[gamepadDeviseDescSize] =

{

    0x18,       // bLenght

    0x01,       // bDescriptorType, Device descriptor

    0x00,0x02,  // bcdUSB, usb 2.0

    0x03,       // bDeviceClass, HID

    0x00,       // bDeviceSubclass, not used

    0x00,       // bDeviceProtocol, not used

    0x40,       // bMaxPacketSize0, maximum size 64 byte

    0xC4,0x10,  // idVendor, SiLabs

    0x00,0x00,  // idProduct

    0x00,0x01,  // bcdDevice

    0x01,       // iManufacter

    0x02,       // iProduct

    0x00,       // iSerialNumber

    0x01        // bNumConfigurations

};

bLenbght = 18 - длина дескриптора в байтах, стандартно первый параметр для любого дескриптора

bDescriptorType = 0x01 - тип дескриптора, так же, стандартно второй параметр дескриптора, единица означает device дескриптор

bcdUSB = 0x0200 - 16 битный параметр, обозначающий текущую версию спецификации USB, у меня уже USB 2.0 (если что, то вышел уже четвертый).

bDeviceClass = 0x03 - класс устройства, в моем случае USB HID, все остальные константы ищите в стандарте.

Далее извините, тут вам не стандарт, не используемые в моем случае параметры описывать не буду.

bMaxPacketSize0 = 0x40 - означает максимально возможный размер в 64 байта, определенный для нулевого endpoint, в котором и происходит вся конфигурация устройства.

idVendor = 0xc410 - для каждого производителя зарегистрирован отдельный ID, как правило это производители чипов, я использую STM.

idProduct = 0x0000 - ID продукта, так же регистрируются USB форумом, от балды тут ничего написать нельзя, пишем то, что советует datasheet нашего чипа.

bcdDevice = 0x0001 - номер релиза устройства по порядку

iManufacter = 0x01 - номер строки содержащей информацию о производителе устройства

iProduct = 0x02 - номер строки, с информацией о самом устройстве

iSerialNumber = 0x00 - строка с серийным номером устройства (в моем случае не используется)

bNumConfigurations = 0x01 - помните про configuration дескриптор, так вот их бывает так же несколько, но в моем случае, само собой один.

const uint8_t gamepadConfigurationDesc[gamepadConfigurationDescSize] =

{

    0x09,       // bLenght

    0x02,       // bDescriptorType, Configuration descriptor

    0x22, 0x00, // TotalLenght

    0x01,       // bNumInterfaces

    0x01,       // bConfigurationValue

    0x00,       // iConfiguration

    0xe0,       // bmAttributes, self-powered

    0x32        // maxPower, 100mA

};

TotalLenght = 0x0022 - общий размер почти всех дескрипторов, что есть (configuration, interface, endpoint 1, hid)

bNumInterfaces = 0x01 - количество интерфейсов, у меня он просто есть, потому что так надо

bConfigurationValue = 0x01 - номер описываемой в дескрипторе конфигурации, что бы host мог их выбирать, в случае если их несколько

iConfiguration = 0x00 - и для каждой конфигурации так же можно определить строку с описанием

bmAttributes = 0xe0 - стандартные функции устройства, в моем случае сообщим только, что устройcтво может может уходить в сон и будиться удаленно и запитано от шины USB (бывают и те что запитаны от отдельного блока питания, отключаемого по тому же USB)  

maxPower = 0x32 максимальный ток, выставим нереальные для моего скромного конфига 100 мА

const uint8_t gamepadInterfaceDesc[gamepadInterfaceDescSize] =

{

    0x09,       // bLenght

    0x04,       // bDescriptorType, Interface descriptor

    0x00,       // bInterfaceNumber

    0x00,       // bAlternateSetting

    0x01,       // bNumEndpoints

    0x03,       // bInterfaceClass, HID

    0x00,       // bInterfaceSubClass,

    0x00,       // bInterfaceProtocol

    0x00        // bInterface

};

bNumEndpoints = 0x01 - общее количество endpoint за исключением endpoint 0, который по умолчанию

bInterfaceClass = 0x03 - на самом деле о том, что у нас HID устройство, нужно говорить тут

const uint8_t gamepadInEndpDesc[gamepadInEndpDescSize] =

{

    0x07,       // bLenght

    0x05,       // bDescriptorType, endpoint descriptor

    0x81,       // bEndpointAddress, IN endpoint 1

    0x03,       // bMattributes, interrupt endpoint

    0x04,0x00,  // MaxPacketSize, 4 bytes

    0x20        // bInterval 32 ms

};

bEndpointAddress = 0x81 -  endpoint с адресом 1 типа IN (направление данных от устройства к хосту)

bMattributes = 0x03 - тип передачи данных по endpoint, в моем случае interrupt endpoint, самый простой, не нагруженный

MaxPacketSize = 0x0004 - максимальный размер пакета в 4 байта, что бы сообщить о состоянии кнопок нужно немного.

bInterval = 0x20 - интервал запроса данных хостом в 32 мс.

const uint8_t stringLangId[stringLangIdSize] =

{

    0x04, 0x03, 0x09, 0x04

};

0x09 English

0x03 U.S. English

const uint8_t gamepadStringVendor[gamepadStringVendorSize] =

{

    0x08,

    0x03,

    'z',0,'x',0

};

const uint8_t gamepadStringProduct[gamepadStringProductSize] =

{

    0x06,

    0x03,

    'j',0,'o',0,'y',0

};

Как вы понимаете, размер дескриптора, тип дескриптора (строка), далее сама строка в двухбайтовом юникоде.

И есть еще совсем чуть чуть дескрипторов, описывающих конкретно мое устройство, в моем случае это простейший геймпад.
  • HID - определяет то, сколько report будет передаваться между хостом и устройством
  • Report - собственно то, ради чего все и затевалось, в моем случае всего один байт информации о нажатых кнопках на джойстике, к слову у мышек три. А вот описывает их дескриптор чуть ли не на все 64 байта.

const uint8_t gamepadHidDesc[gamepadHidDescSize] =

{

    0x09,       // bLenght

    0x21,       // bDescriptorType, HID descriptor

    0x00,0x01,  // bcdHID, HID spec 1.01

    0x33,       // country code SU

    0x01,       // bNumDescriptors

    0x22,       // bDescriptorType, report descriptor

    0x55,0x00  // wDescriptorLenght

};

bcdHID = 0x0101 - версия HID спецификации, которую читал

country code = 0x33 - региональный код, нужен для клавиатур, и там даже есть советский союз

wDescriptorLenght = 0x0055 - размер report дескриптора для этого устройства

const uint8_t gamepadReportDesc[gamepadreportDescSize] =

{

    0x05, 0x01,         // usage_page(Generic Desctop)

    0x09, 0x05,         // usage(Game pad)

    0xa1, 0x01,         // collection(Application)

    0x05, 0x01,         //  usage_page(Generic Desctop)

    0x09, 0x01,         //  usage(Pointer)

    0xa1, 0x00,         //   collection(Physical)

    0x09, 0x39,         //   usage(Hat switch)

    0x15, 0x00,         //   logical minimum(0)

    0x25, 0x07,         //   logical maximum(7)

    0x35, 0x00,         //   physical minimum(0)

    0x46, 0x3b, 0x01,   //   physical maximum(315)

    0x65, 0x14,         //   unit(Eng Rot:Angular Pos)

    0x95, 0x01,         //   report_count(1)

    0x75, 0x04,         //   report_size(4)

    0x81, 0x42,         //   input(Data,Var,Abs,Null)

    0xc0,               //  end_collection

    0x05, 0x09,         //  usage_page(Button)

    0x15, 0x00,         //  logical minimum(0)

    0x25, 0x01,         //  logical maximum(1)

    0x95, 0x02,         //  report_count(2)

    0x75, 0x01,         //  report_size(1)

    0x81, 0x02,         //  input(Data,Var,Abs)

// byte alingment

    0x95, 0x01,         //  report_count(1)

    0x75, 0x02,         //  report_size(2)

    0x81, 0x01,         //  input(Const)

    0xc0                // end_collection

};

Если совсем коротко, то в туториале на google code назвали то, что происходит в report descriptor мясом, а я бы сказал что там какой то фарш. Подробно опишу в следующей статье. Здесь только скажу, что данный дескриптор сгенерирован стандартной утилитой (под вайном юзабельно). В нем подробно описаны типы данных, которые будут передаваться собственно в самих report.

А теперь о самих запросах, в минимальной стандартной конфигурации их определено десять. Но на самом деле полезны только два, тот что нумерует устройство, и тот что запрашивает дескрипторы. Все остальное, как обычно нужно как то реализовать для полной совместимости со стандартом, проще говоря что бы было.

Комментариев нет :

Отправить комментарий