Linux-USB Host Side API Печать
Добавил(а) microsin   

Universal Serial Bus (USB) используется для подключения к хосту, такому как PC или рабочая станция, некоторого количества периферийных устройств. USB использует древовидную структуру шины, где хост это корень дерева (root, главное устройство, system master), хабы работают как внутренние узлы, а периферийные устройства как листья (и подчиненные устройства, slave). Современные PC из коробки поддерживают несколько таких деревьев устройств USB, обычно несколько шин USB 3.0 (5 GBit/s) или USB 3.1 (10 GBit/s), и некоторые legacy USB 2.0 (480 MBit/s).

Такая асиметрия master/slave была разработана по нескольким причинам, одна из которых простота использования. Физически невозможно ошибиться в восходящем (upstream) и нисходящем (downstream) направлении, или это не имеет значения с вилкой типа C (или они встроены в периферию). Кроме того, программное обеспечение хоста не должно иметь дело с распределенной автоматической конфигурацией, поскольку всем этим управляет предварительно назначенный главный узел (master шины).

Разработчики ядра добавили поддержку USB в Linux в начале серии 2.2 kernel и с тех пор развивали её дальше. Помимо поддержки каждого нового поколения USB, получили поддержку различные хост-контроллеры, были добавлены новые драйверы для периферийных устройств и введены расширенные функции для измерения задержки и улучшенного управления питанием.

Linux может работать как внутри устройств USB, так и на хостах, которые управляют этими устройствами. Однако драйвера USB, работающие внутри этих периферийных устройств совсем не то же самое, что работает внутри хостов, поэтому им дано другое имя: драйверы гаджетов (gadget drivers). В этой статье (перевод документации [1]) gadget drivers не рассматриваются.

[USB Host-Side API Model]

Host-side драйвера для устройств USB взаимодействуют с "usbcore" API, которых 2 разновидности. Один API предназначен для драйверов общего назначения (general-purpose drivers, представленных через фреймворки драйверов), и другой API для драйверов, которые составляют часть ядра. Такие драйверы ядра включают драйвер хаба (hub driver, который управляет деревом устройств USB) и несколько разного вида драйверов контроллеров, которые управляют отдельными шинами.

Модель устройства, которую видят драйверы USB, относительно сложна.

● USB поддерживает 4 вида транзакций данных (control, bulk, interrupt и isochronous). Две из них (control и bulk) используют полосу шины по мере доступности, в то время как для двух других (interrupt и isochronous) запланирована гарантированная полоса пропускания.
● Модель описания устройства включает одну или несколько "конфигураций" на одном устройстве, из которых в любой момент времени активной может быть только одна. Предполагается, что устройства способны работать на более низких скоростях, чем их самая большая скорость, и могут предоставлять дескриптор BOS, показывающий самую низкую скорость, на которой они остаются полностью работоспособными.
● Начиная с USB 3.0 на конфигурациях есть одна или несколько "функций", которые обеспечивают общую функциональность и сгруппированы вместе в целях управления питанием.
● Конфигурации или функции имеют один или несколько "интерфейсов", каждый из которых могут иметь "альтернативные настройки". Интерфейсы могут быть стандартизованы в соответствии со спецификациями "класса" USB, или могут быть специфичны для вендора или устройства.

Драйверы устройства USB фактически привязаны к интерфейсам, не к устройствам. Думайте о них как о "драйверах интерфейса", хотя вы можете не увидеть много устройств, где важно различие. Большинство USB-устройств просты, имеют только одну функцию, одну конфигурацию, один интерфейс и одну альтернативную настройку.

● У интерфейсов есть одна или несколько "конечных точек" (endpoint), каждая из которых поддерживают один тип и одно направление передачи данных, такие как "bulk out" или "interrupt in". Вся конфигурация может иметь до 16 конечных точек в каждом направлении, распределяемых по мере необходимости между всеми интерфейсами.
● Транзакции данных на USB оформлены в пакеты; каждая конечная точка имеет максимальный размер пакета. Драйверы часто должны знать о таких соглашениях, как пометка конца bulk-транзакций, используя "short" пакеты (включая пакеты нулевой длины).
● Linux USB API поддерживает синхронные вызовы для сообщений control (управления) и bulk (быстрая передача больших объемов данных). Linux USB API также поддерживает асинхронные вызовы для всех видов транзакций данных, используя структуры запроса, называемые "URB" (USB Request Blocks).

Проще говоря, URB это "пакет с заданием" или "команда", которую драйвер USB-устройства передает низкоуровневому драйверу хост-контроллера (HCD) для выполнения какой-либо операции обмена данными по шине USB.

Представьте себе иерархию драйверов USB:

1. Драйвер устройства (например, для вашей флешки или веб-камеры)
2. Ядро USB (USB Core) — общий код, управляющий всей подсистемой
3. Драйвер хост-контроллера (HCD) — который напрямую общается с "железом"

URB — это стандартный структурированный объект ядра Linux, который используется для общения между уровнями 1 и 3.

Когда драйверу устройства нужно что-то сделать (отправить данные, получить данные, изменить настройки), он:

1. Создает или получает URB.

2. Заполняет его всей необходимой информацией:

  - Тип запроса (чтение, запись, управление)
  - Адрес устройства и номер конечной точки (endpoint)
  - Указатель на буфер данных (откуда брать или куда класть данные)
  - Длину данных
  - Функцию обратного вызова (callback) — которая будет вызвана, когда операция завершится (успешно или с ошибкой)

3. Отправляет ("submits") этот URB в ядро USB.

4. Ядро и HCD выполняют необходимую низкоуровневую работу, чтобы выполнить этот запрос на физической шине USB.

5. Когда операция завершена (данные получены, отправлены или произошла ошибка), HCD вызывает функцию обратного вызова этого URB, чтобы уведомить драйвер устройства о результате.

URB — это фундаментальный механизм в Linux (и в похожем виде в других ОС) для организации любого обмена данными по шине USB. Это структура, которая инкапсулирует один запрос на передачу данных между хостом и USB-устройством.

Соответственно USB Core API, предоставленный для драйверов устройств, охватывает довольно большую территорию. Вам вероятно понадобится проконсультироваться со спецификацией USB 3.0, бесплатно доступной на www.usb.org, а также со спецификациями класса устройства.

Единственными драйверами на стороне хоста, которые фактически работают с железом (читают/записывают регистры контроллера, обрабатывают IRQ, и так далее), являются драйверы HCD (Host Controller Driver). Теоретически все HCD предоставляют одинаковый функционал через одно и то же API. На практике это также верно, однако все еще есть различия, которые возникают время от времени, особенно при обработке ошибок на менее распространенных контроллерах. Различные контроллеры не обязательно сообщают об одних и тех же аспектах сбоев, и восстановление после сбоев (включая теми, которые вызваны программным обеспечением, таких как отключение URB) пока еще не согласовано полностью. Авторы драйверов устройств должны провести тестирование отключения (когда устройство активно) с каждым отдельным драйвером HCD, чтобы убедиться, что у драйверов нет собственных ошибок, а также чтобы убедиться, что они не полагаются на какое-то особенное поведение, специфичное для определенного HCD.

HCD расшифровывается как Host Controller Driver (Драйвер Хост-Контроллера).

[Что такое HCD]

● Host Controller (Хост-Контроллер) — это аппаратный чип на материнской плате компьютера (или внутри процессора/чипсета), который физически управляет шиной USB. Он отвечает за низкоуровневые операции: генерацию сигналов, управление передачей данных, обработку прерываний (IRQ).
● Driver (Драйвер) — это программа, которая позволяет операционной системе взаимодействовать с этим аппаратным обеспечением.

Таким образом, HCD — это самый низкоуровневый драйвер в стеке USB, который "общается" напрямую с аппаратным обеспечением хост-контроллера.

Представьте себе иерархию (стек) драйверов USB:

1. Клиентские драйверы (например, драйвер принтера, клавиатуры, флешки): jни работают на высоком уровне и "думают" в терминах "распечатать страницу" или "прочитать файл". Они не знают, как физически устроен USB.

2. Драйверы USB-устройств (например, `usbstor.sys` для флешек): jни стандартизируют взаимодействие для классов устройств.

3. USB-ядро (USB Core): общая часть ОС, которая управляет всем USB-хостом, предоставляет API для драйверов устройств.

4. HCD (Host Controller Driver): это единственное звено в этой цепочке, которое знает, как именно "дергать" регистры конкретного хост-контроллера, обрабатывать его прерывания и передавать ему данные для отправки по шине.

[Примеры HCD]

В мире существует несколько основных типов хост-контроллеров, и для каждого из них нужен свой HCD:

● UHCI (Universal Host Controller Interface): разработан Intel, в основном для USB 1.x.
● OHCI (Open Host Controller Interface): разработан Compaq, Microsoft и др., альтернатива UHCI для USB 1.x.
● EHCI (Enhanced Host Controller Interface): стандарт для USB 2.0.
● xHCI (eXtensible Host Controller Interface): единый современный стандарт для USB 3.x и выше, который поддерживает все скорости (от Low-Speed до SuperSpeed+).

В современных операционных системах (Windows, Linux, macOS) соответствующие HCD (xhci.sys, uhci_hcd, ohci_hcd, ehci_hcd) встроены в ядро.

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

[Стандартные типы USB]

В заголовочном файле include/uapi/linux/usb/ch9.h вы найдете типы данных USB, определенные в главе 9 спецификации USB. Эти типы данных используются во всем ПО USB и интерфейсах API, включая host side API, gadget API, интерфейсы символьных устройств (usb character devices) и интерфейсы файловой системы отладки (debugfs). Сам этот файл подключен в include/linux/usb/ch9.h, который также содержит декларации и несколько подпрограмм утилит для манипуляции этими типами данных; их реализации находятся в drivers/usb/common/common.c.

const char *usb_ep_type_string (int ep_type)

Возвратит удобное для чтения имя типа конечной точки.

Параметры:

int ep_type Тип конечной точки. Если это не любой из известных типов: USB_ENDPOINT_XFER_{CONTROL, ISOC, BULK, INT}, обычно получаемый из usb_endpoint_type(), то будет возвращена строка 'unknown'.

const char *usb_otg_state_string (enum usb_otg_state state)

Возвратит удобное для чтения имя состояния OTG.

Параметры:

enum usb_otg_state state Состояние OTG. Если это не одно из состояний, определенных перечислением usb_otg_state, то будет возвращено 'UNDEFINED'.

Перечисление usb_otg_state в ядре Linux описывает различные состояния, через которые проходит устройство с поддержкой технологии USB On-The-Go (OTG). Эта технология позволяет устройству динамически переключаться между ролью USB-хоста (который управляет другими устройствами, как компьютер) и периферийного устройства (которым управляют, как флешка).

[Что такое USB OTG]

USB On-The-Go (OTG) — это расширенная спецификация USB, которая позволяет портативному устройству (например, смартфону или планшету) выполнять роль USB-хоста и подключать к себе другую периферию: флешки, клавиатуры, мыши или жесткие диски. Устройства с OTG могут менять роли "на ходу", что и отражено в названии.

[Состояния OTG (usb_otg_state)]

Состояния, определенные в перечислении usb_otg_state, можно разделить на три группы в зависимости от текущей роли устройства.

Состояния для периферийного устройства (Default-B):

OTG_STATE_B_IDLE — начальное состояние, устройство неактивно.
OTG_STATE_B_SRP_INIT — устройство инициирует процесс SRP (Session Request Protocol), чтобы попросить хост (например, компьютер) включить питание VBUS и начать сессию.
OTG_STATE_B_PERIPHERAL — устройство работает в качестве обычной USB-периферии.

Дополнительные состояния для Dual-Role устройства (Default-B):

OTG_STATE_B_WAIT_ACON — устройство ожидает подключения другого периферийного устройства.
OTG_STATE_B_HOST — устройство выполняет роль хоста.

Состояния для хоста (Default-A):

OTG_STATE_A_IDLE — начальное состояние для роли хоста.
OTG_STATE_A_WAIT_VRISE — хост ожидает, пока напряжение на линии VBUS поднимется до необходимого уровня после включения питания.
OTG_STATE_A_WAIT_BCON — хост ожидает подключения периферийного устройства.
OTG_STATE_A_HOST — хост активно работает, управляя подключенным устройством.
OTG_STATE_A_SUSPEND — хост переведен в состояние suspEND.
OTG_STATE_A_PERIPHERAL — хост переключился на роль периферийного устройства (посредством HNP).
OTG_STATE_A_WAIT_VFALL — хост ожидает падения напряжения на VBUS в конце сессии.
OTG_STATE_A_VBUS_ERR — состояние ошибки, связанное с напряжением VBUS.

[Как работает переключение ролей (HNP)]

Протокол HNP (Host Negotiation Protocol) — это механизм, позволяющий двум устройствам с поддержкой OTG автоматически договариваться о том, кто в данный момент будет хостом, а кто — периферией, без физического переподключения кабеля. Перечисление usb_otg_state отслеживает все этапы этого сложного процесса.

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

const char *usb_speed_string (enum usb_device_speed speed)

Возвратит удобочитаемое имя скорости USB.

Параметры:

enum usb_device_speed speed Если скорость speed не определена в перечислении usb_device_speed, то будет возвращена строка для USB_SPEED_UNKNOWN.

enum usb_device_speed usb_get_maximum_speed (struct device *dev)

Получение максимальной запрошенной скорости для указанного контроллера USB.

Параметры:

struct device *dev Указатель на устройство контроллера USB.

Описание:

Эта функция получит строку максимальной скорости из свойства "maximum-speed", и возвратит соответствующее перечисление usb_device_speed.

enum usb_ssp_rate usb_get_maximum_ssp_rate (struct device *dev)

Получение информации о генерации скорости передачи сигналов и и количестве каналов устройства, поддерживающего SuperSpeed Plus.

Параметры:

struct device *dev Указатель на устройство контроллера USB.

Описание:

Если строка из свойства "maximum-speed" имеет значение super-speed-plus-genXxY, где "X" - номер генерации, а "Y" - количество полос, то эта функция возвращает соответствующее значение перечисления usb_ssp_rate.

enum usb_ssp_rate это перечисление в ядре Linux, которое определяет скорости передачи данных для стандарта USB SuperSpeed Plus (USB 3.2 Gen 2) и выше.

[Что такое USB SuperSpeed Plus (SSP)]

USB SSP это эволюция стандарта USB 3.0/SuperSpeed, которая увеличивает максимальную скорость передачи данных:

- USB 3.2 Gen 1 (SuperSpeed) → до 5 Гбит/с
- USB 3.2 Gen 2 (SuperSpeed Plus) → до 10 Гбит/с
- USB 3.2 Gen 2×2 → до 20 Гбит/с
- USB4 → до 40 Гбит/с

В ядре Linux enum usb_ssp_rate содержит следующие основные значения:

enum usb_ssp_rate {
USB_SSP_GEN_UNKNOWN = 0,
USB_SSP_GEN_2x1, // 10 Гбит/с (USB 3.2 Gen 2)
USB_SSP_GEN_1x2, // 10 Гбит/с (альтернативная конфигурация)
USB_SSP_GEN_2x2, // 20 Гбит/с (USB 3.2 Gen 2×2)
// Для будущих стандартов (USB4)
USB_SSP_GEN_4x1, // 40 Гбит/с (USB4)
USB_SSP_GEN_3x2, // 40 Гбит/с (альтернативная конфигурация) };

В ядре сборок Linux для встраиваемых устройств (например LuckFox Buildroot [3]) этих значений может быть меньше.

[Где используется usb_ssp_rate]

Это перечисление используется в различных структурах данных ядра Linux:

- struct usb_device — для хранения информации о скорости подключенного устройства
- struct usb_host_interface — для описания возможностей интерфейсов
- Драйверы хост-контроллеров (xHCI) — для настройки контроллера на работу с соответствующими скоростями
- Протокол обнаружения Link Training — для автоматического согласования скорости между устройством и хостом

Практическое значение. Для пользователей и разработчиков это означает:

- Автоматическое определение скорости — система сама определяет максимальную поддерживаемую скорость соединения
- Обратная совместимость — устройства могут работать на более низких скоростях, если максимальная не поддерживается
- Будущая расширяемость — архитектура готова к появлению более высокоскоростных стандартов USB.

Пример использования. Когда вы подключаете современное USB-устройство (например, SSD-накопитель), драйвер хост-контроллера использует это перечисление для:

1. Обнаружения — какая скорость поддерживается устройством
2. Согласования — выбор оптимальной скорости соединения
3. Настройки — конфигурации контроллера для работы на выбранной скорости

Таким образом, enum usb_ssp_rate является важным компонентом системы, обеспечивающим поддержку высокоскоростных режимов USB в современных компьютерах и устройствах.

const char *usb_state_string (enum usb_device_state state)

Возвратит удобочитаемое имя состояния устройства USB.

Параметры:

enum usb_device_state state Если state не соответствует ни одному из значений перечисления usb_device_state, то будет возвращена строка UNKNOWN.

enum usb_device_state — перечисление в ядре Linux, которое описывает логическое состояние USB-устройства в процессе его обнаружения, настройки и работы с момента подключения до отключения.

[Обзор состояний устройства]

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

Вот основные состояния, определенные в enum usb_device_state:

enum usb_device_state {
USB_STATE_NOTATTACHED = 0, // Устройство не подключено
USB_STATE_ATTACHED, // Устройство физически подключено
USB_STATE_POWERED, // Питание подано, но устройство не сконфигурировано
USB_STATE_RECONNECTING, // Попытка переподключения
USB_STATE_UNAUTHENTICATED, // Устройство подключено, но не аутентифицировано
// (для высокоскоростных устройств)
USB_STATE_DEFAULT, // Устройство сброшено, отвечает на нулевой адрес
USB_STATE_ADDRESS, // Устройству назначен уникальный адрес
USB_STATE_CONFIGURED, // Устройство полностью сконфигурировано и готово к работе
USB_STATE_SUSPENDED, // Устройство в режиме приостановки (suspend) };

Типичная последовательность состояний. Когда вы подключаете USB-устройство, оно проходит через следующие состояния:

1. USB_STATE_NOTATTACHED → Устройство не подключено
2. USB_STATE_ATTACHED → Обнаружено физическое подключение
3. USB_STATE_POWERED → Подано питание на устройство
4. USB_STATE_DEFAULT → Устройство сброшено, отвечает на адрес 0
5. USB_STATE_ADDRESS → Устройству назначен уникальный адрес (1-127)
6. USB_STATE_CONFIGURED → Устройство сконфигурировано, интерфейсы активированы

Это перечисление используется в ключевых структурах данных ядра:

- struct usb_device — поле state содержит текущее состояние устройства
- Драйверы хост-контроллера (HCD) — для управления процессом перечисления подключенного устройства USB
- USB core — для отслеживания состояния всех подключенных устройств
- Драйверы устройств — для проверки готовности устройства к работе

[Особые состояния]

- USB_STATE_UNAUTHENTICATED — важно для высокоскоростных устройств, которые требуют дополнительной аутентификации
- USB_STATE_SUSPENDED — режим энергосбережения, когда устройство не активно
- USB_STATE_RECONNECTING — состояние при попытке восстановления соединения после сбоя

[Практическое значение usb_device_state]

Понимание этих состояний помогает:

- Разработчикам драйверов — правильно обрабатывать инициализацию устройств
- Системным администраторам — диагностировать проблемы с USB-устройствами
- При отладке — определять, на каком этапе инициализации происходит сбой

Например, если устройство "зависло" в состоянии USB_STATE_ADDRESS, это может означать проблему с конфигурацией дескрипторов, а состояние USB_STATE_UNAUTHENTICATED может указывать на проблемы с определением скорости устройства.

Это перечисление является фундаментальным для понимания того, как Linux управляет USB-устройствами на логическом уровне.

enum usb_dr_mode usb_get_role_switch_default_mode (struct device *dev)

Получение режима по умолчанию для указанного устройства.

Параметры:

struct device *dev Указатель на структуру запрашиваемого устройства.

Описание:

Эта функция берет строку из свойства role-switch-default-mode, и возвращает соответствующее значение перечисления usb_dr_mode.

enum usb_dr_mode — перечисление в ядре Linux, которое определяет режим работы USB-контроллера в системах с поддержкой Dual-Role (двойной роли), таких как USB On-The-Go (OTG) или USB Dual-Role Device (DRD).

[Основные режимы работы]

Перечисление определяет, в какой роли может работать USB-порт:

enum usb_dr_mode {
USB_DR_MODE_UNKNOWN = 0,
USB_DR_MODE_HOST, // Только роль хоста
USB_DR_MODE_PERIPHERAL, // Только роль устройства
USB_DR_MODE_OTG, // Dual-Role с поддержкой OTG };

Детальное описание режимов:

USB_DR_MODE_HOST

- Порт работает только как USB-хост
- Может подключать и управлять периферийными устройствами (флешки, мыши, клавиатуры)
- Типично для портов на компьютерах, ноутбуках, Raspberry Pi

USB_DR_MODE_PERIPHERAL

- Порт работает только как USB-устройство
- Может подключаться к хосту (компьютеру)
- Типично для смартфонов, планшетов, Arduino при подключении к ПК

USB_DR_MODE_OTG

- Порт поддерживает **двойную роль** (Dual-Role)
- Может автоматически или программно переключаться между ролью хоста и устройства
- Поддерживает протоколы SRP и HNP для переговоров о роли

[Где используется usb_dr_mode]

Device Tree (дерево устройств):

/* Пример определения в Device Tree */
usb0: usb@12345678 {
compatible = "chip,usb-otg";
dr_mode = "otg"; // или "host", "peripheral"
status = "okay"; };

Структуры данных ядра:

- struct usb_phy — для PHY-контроллеров
- struct usb_hcd — для хост-контроллеров
- struct gadget — для периферийных устройств
- Драйверы SoC (System-on-Chip) для настройки USB-контроллера

[Практические примеры]

Смартфон:

- Обычно имеет порт в режиме USB_DR_MODE_OTG
- При подключении к ПК → работает как устройство (MTP, charging)
- При подключении флешки → работает как хост

Одноплатный компьютер (Raspberry Pi, LuckFox и т. п.):

- USB-порты обычно в режиме USB_DR_MODE_HOST
- Могут подключать клавиатуры, мыши, флешки

Специализированные устройства:

- USB-модемы → USB_DR_MODE_PERIPHERAL
- USB-хабы → USB_DR_MODE_HOST

[Как определяется режим]

Режим может задаваться несколькими способами:

1. Аппаратно — перемычками на плате, конфигурационными пинами
2. Через Device Tree — статическая конфигурация при компиляции
3. Динамически — через sysfs или специальные драйверы
4. Автоматически — через протоколы OTG (HNP/SRP)

[Значение для разработчиков]

Понимание этого перечисления важно для:

- Разработчиков драйверов — правильная инициализация контроллера
- Системных интеграторов — конфигурация Device Tree для конкретного применения
- Отладки проблем — определение, почему порт не работает в нужном режиме

Например, если порт сконфигурирован как USB_DR_MODE_PERIPHERAL, он не сможет работать с флешками, даже если аппаратно это возможно.

Это перечисление является ключевым для правильной работы USB-подсистемы в современных embedded-системах и мобильных устройствах.

unsigned int usb_decode_interval (const struct usb_endpoint_descriptor *epd, enum usb_device_speed speed)

Декодирует bInterval во время, выраженное в микросекундах.

Параметры:

const struct usb_endpoint_descriptor *epd Дескриптор конечной точки.

enum usb_device_speed speed Скорость, на которой работает конечная точка.

Описание:

Функция возвратит интервал в микросекундах обслуживания endpoint для транзакций данных.

enum usb_dr_mode of_usb_get_dr_mode_by_phy (struct device_node *np, int arg0)

Получение режима двойной роли для устройства контроллера, связанного с указанным физическим device_node.

Параметры:

struct device_node *np Указатель на phy device_node.

int arg0 phandle args[0] устройств phy с #phy-cells >= 1, или -1 для устройств phy, у которых нет phy-cells.

Описание:

В dts контроллер usb связывается с физическими устройствами (phy devices). Функция берет строку из свойства 'dr_mode' контроллера, связанного с указанным phy device node, и возвращает соответствующее значение usb_dr_mode.

bool of_usb_host_tpl_support (struct device_node *np)

Применяется для получения информации о том, поддерживается ли список целевых периферийных устройств для заданных целевых хостов (хостов, отличных от ПК).

Параметры:

struct device_node *np Указатель на структуру device_node.

Описание:

Функция определит, поддерживает ли хост TPL, или не поддерживает.

TPL в контексте функции of_usb_host_tpl_support означает Targeted Peripheral List (Целевой Список Периферийных Устройств).

[Что такое TPL]

TPL это механизм в спецификации USB, который позволяет:

- Хост-контроллеру предварительно объявить, какие типы периферийных устройств он может поддерживать
- Ограничить или оптимизировать процесс перечисления USB-устройств
- Ускорить процесс подключения устройств

Основная цель TPL. TPL решает проблему, когда хост-контроллер имеет ограниченные ресурсы или специализированные требования, и хочет работать только с определенными типами устройств.

[Техническая реализация]

Структура TPL. TPL содержит дескрипторы поддерживаемых устройств:

- Class codes (коды классов устройств)
- Vendor ID (VID) и Product ID (PID)
- Информацию Protocol и Subclass

Как работает:

1. Хост объявляет свой TPL
2. При подключении устройства происходит быстрая проверка по TPL
3. Если устройство есть в списке — ускоренное перечисление
4. Если устройства нет в списке — стандартное перечисление или отказ

[Использование в функции of_usb_host_tpl_support]

Функция bool of_usb_host_tpl_support(struct device_node *np):

/**
* of_usb_host_tpl_support - проверяет поддержку TPL в Device Tree
* @np: device node USB хоста
*
* Возвращает: true если хост поддерживает TPL, false в противном случае
*/
bool of_usb_host_tpl_support(struct device_node *np) {
// Чтение свойства "tpl-support" из Device Tree
return of_property_read_bool(np, "tpl-support"); }

Пример из Device Tree:

/* Пример конфигурации USB хоста с TPL */
usb_controller: usb@12340000 {
compatible = "vendor,usb-host";
reg = < 0x12340000 0x1000>;
tpl-support; /* Указывает поддержку TPL */

/* Опционально: конкретные устройства в TPL */
tpl-devices = <
/* VID, PID, Class, Subclass, Protocol */
0x1234 0x5678 0x08 0x06 0x50 /* Конкретное устройство */
0x0000 0x0000 0x08 0x00 0x00 /* Все устройства класса Mass Storage */
>; };

[Практическое применение]

Встроенные системы:

- IoT устройства — поддерживают только конкретные датчики
- Автомобильные системы — только утвержденные устройства
- Медицинское оборудование — только сертифицированные периферийные устройства

Оптимизация:

- Сокращение времени загрузки — быстрая проверка вместо полного перечисления
- Экономия ресурсов — не тратятся ресурсы на неподдерживаемые устройства
- Повышение безопасности — предотвращение подключения неавторизованных устройств

[Преимущества TPL]

1. Ускоренное подключение устройств из белого списка
2. Предсказуемое поведение системы
3. Энергоэффективность — не тратится энергия на ненужные устройства
4. Безопасность — защита от неавторизованных USB-устройств

Таким образом, TPL — это важный механизм для оптимизированных и безопасных USB-реализаций во встроенных системах и специализированных устройствах.

int of_usb_update_otg_caps (struct device_node *np, struct usb_otg_caps *otg_caps)

Применяется для обновления возможностей USB OTG (capabilities) в соответствии со свойствами, переданными в DT (Device Tree).

Параметры:

struct device_node *np Указатель на структуру device_node.

struct usb_otg_caps *otg_caps Указатель на целевое устанавливаемое usb_otg_caps.

Описание:

Функция обновляет otg capabilities.

[DT (Device Tree)]

Device Tree (Дерево Устройств) это структура данных, которая описывает аппаратное обеспечение системы без необходимости изменять код ядра.

/* Пример узла USB OTG в Device Tree */
usb_otg: usb@12345678 {
compatible = "vendor,usb-otg";
reg = < 0x12345678 0x1000>;
interrupts = < 0 45 4>;

/* Свойства OTG capabilities */
otg-capabilities;
hnp-capable; /* Поддержка HNP */
srp-capable; /* Поддержка SRP */
adp-capable; /* Поддержка ADP */
usb-role-switch; /* Переключение ролей */ };

Назначение Device Tree:

- Описание "железа" — регистры, прерывания, частоты
- Платформонезависимость — один образ ядра для разных устройств
- Динамическая конфигурация — без перекомпиляции ядра

[usb_otg_caps (USB OTG Capabilities)]

struct usb_otg_caps это структура в ядре Linux, которая описывает возможности USB OTG контроллера.

Структура usb_otg_caps:

struct usb_otg_caps {
unsigned short otg_rev; /* Версия OTG спецификации */
bool hnp_support; /* Поддержка HNP */
bool srp_support; /* Поддержка SRP */
bool adp_support; /* Поддержка ADP */
bool otg_1_3_support; /* Поддержка OTG 1.3+ */
bool role_switch_support; /* Поддержка переключения ролей */
bool extcon_support; /* Использование extcon для уведомлений */ };

HNP (Host Negotiation Protocol):

- Автоматическое переключение ролей хост↔устройство
- Без физического переподключения кабеля

SRP (Session Request Protocol):

- Устройство может "попросить" хост начать сессию
- Энергосберегающая технология

ADP (Attach Detection Protocol):

- Обнаружение подключения/отключения устройств
- Альтернатива механическим детекторам в кабеле

[Функция of_usb_update_otg_caps]

/**
* of_usb_update_otg_caps - обновляет возможности OTG из Device Tree
* @np: device node USB контроллера
* @otg_caps: структура для заполнения возможностями OTG
*
* Возвращает: 0 при успехе, отрицательный код ошибки при failure
*/
int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps) {
/* Чтение свойств из DT и обновление otg_caps */
otg_caps->hnp_support = of_property_read_bool(np, "hnp-capable");
otg_caps->srp_support = of_property_read_bool(np, "srp-capable");
otg_caps->adp_support = of_property_read_bool(np, "adp-capable");
otg_caps->role_switch_support = of_property_read_bool(np, "usb-role-switch");

return 0; }

[Практическое использование]

В драйверах SoC:

struct usb_otg_caps otg_caps = {0};

/* Инициализация базовых возможностей */ otg_caps.otg_rev = 0x0200;

/* Обновление из Device Tree */ of_usb_update_otg_caps(dev->of_node, &otg_caps);

/* Использование в USB контроллере */ usb_phy_set_otg_caps(phy, &otg_caps);

В Device Tree специфичного устройства:

/* Для устройства с полной поддержкой OTG */
usb_otg: usb@12340000 {
compatible = "vendor,advanced-otg";
hnp-capable;
srp-capable;
adp-capable;
usb-role-switch; };

/* Для устройства с ограниченной поддержкой */
usb_otg: usb@12350000 {
compatible = "vendor,basic-otg";
srp-capable; /* Только SRP поддержка */ };

Функция of_usb_update_otg_caps позволяет:

- Единообразно конфигурировать OTG возможности across разных платформ
- Избежать хардкода в драйверах
- Легко адаптировать под конкретное аппаратное обеспечение
- Поддерживать optional фичи через свойства в DT

Таким образом, связка Device Tree + usb_otg_caps обеспечивает гибкий и мощный механизм конфигурации USB OTG возможностей в современных embedded-системах.

struct device *usb_of_get_companion_dev (struct device *dev)

Находит companion-устройство.

Параметры:

struct device *dev Указатель на структуру device для поиска компаньона.

Описание:

Функция находит companion-устройство от шины платформы. Принимает ссылку на возвращенное устройство структуры, которое необходимо удалить после использования.

Возвращаемое значение: в случае успеха указатель на companion device, NULL при неудаче.

Кроме того, некоторые функции, полезные для создания выходных данных отладки, определены в drivers/usb/common/debug.c.

Companion Device (компаньон-устройство) в контексте USB это концепция, связанная с архитектурой USB 2.0 EHCI (Enhanced Host Controller Interface) и разделением скоростей работы.

[Архитектурный контекст]

Проблема:

- EHCI контроллер поддерживает только high-speed устройства (480 Мбит/с)
- Full-speed (12 Мбит/с) и low-speed (1.5 Мбит/с) устройства не могут работать напрямую с EHCI

Решение: система использует компаньон-контроллеры:

- UHCI (Universal Host Controller Interface) или
- OHCI (Open Host Controller Interface)

.. которые работают параллельно с EHCI для обслуживания низкоскоростных устройств.

[Что такое Companion Device]

Companion Device — это дополнительный хост-контроллер, который:

- Работает в паре с основным EHCI контроллером
- Обслуживает full-speed и low-speed устройства
- Разделяет общую шину USB с основным контроллером
- Имеет общую систему питания и обнаружения подключения

Структура системы:

         USB Physical Port
               │
        ┌──────┴──────┐
        │             │
    EHCI Controller   │  (high-speed only)
        │             │
        │      Companion Controller
        │             │  (UHCI/OHCI - full/low-speed)
        │             │
    High-speed     Full/Low-speed
    Devices        Devices

[Функция usb_of_get_companion_dev]

/** * usb_of_get_companion_dev - находит компаньон-устройство через Device Tree
* @dev: основное USB устройство (обычно EHCI контроллер)
*
* Возвращает: указатель на device компаньона или NULL если не найден
*/
struct device *usb_of_get_companion_dev(struct device *dev) {
// Ищет в Device Tree связанное компаньон-устройство
// через стандартные свойства "companion", "companion-controller" }

[Пример из Device Tree]

/* Основной EHCI контроллер */
ehci0: usb@12340000 {
compatible = "generic-ehci";
reg = < 0x12340000 0x1000>;
interrupts = < 0 45 4>;

/* Ссылка на компаньон-контроллер */
companion = < &uhci0>; };

/* Компаньон UHCI контроллер */
uhci0: usb@12350000 {
compatible = "generic-uhci";
reg = < 0x12350000 0x1000>;
interrupts = < 0 46 4>;

/* Ссылка на основной контроллер */
companion = < &ehci0>; };

[Практическое использование]

/* В драйвере EHCI контроллера */
struct device *ehci_dev = &pdev->dev;
struct device *companion_dev;

/* Поиск компаньон-устройства */ companion_dev = usb_of_get_companion_dev(ehci_dev);
if (companion_dev) {
/* Синхронизация настроек питания */
/* Общая инициализация портов */
/* Координация управления шиной */ }

[Для чего нужен Companion Device]

Автоматическое перенаправление:

- High-speed устройства → обслуживаются EHCI
- Full/low-speed устройства → автоматически перенаправляются на компаньон-контроллер

Общие ресурсы:

- Порты — физические разъемы общие
- Питание — общая система управления питанием VBUS
- Обнаружение — общий механизм обнаружения подключения

Современное развитие. В современных системах с xHCI (eXtensible Host Controller Interface) концепция компаньон-контроллеров менее актуальна, так как xHCI поддерживает все скорости (от low-speed до SuperSpeed) в одном контроллере.

Значение для разработчиков. Понимание компаньон-устройств важно для:

- Разработки драйверов USB host контроллеров
- Конфигурации Device Tree для многоконтроллерных систем
- Отладки проблем с низкоскоростными устройствами
- Оптимизации производительности USB подсистемы

Таким образом, Companion Device — это ключевой компонент архитектуры USB 2.0, обеспечивающий совместимость со всеми типами USB-устройств через разделение контроллеров по скоростям.

[Host-Side типы данных и макросы]

Host side API выставляет несколько слоев для драйверов, некоторое из них более необходимы, чем другие. Они поддерживают модели жизненного цикла для драйверов на стороне и хоста и устройств, и поддерживают передачу буферов через usbcore на некоторый HCD, который выполняет I/O для драйвера устройства.

struct usb_host_endpoint

Дескриптор конечной точки (endpoint) и очередь (queue) на стороне хоста.

Определение:

struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct usb_ss_ep_comp_descriptor ss_ep_comp;
struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp;
struct usb_eusb2_isoc_ep_comp_descriptor eusb2_isoc_ep_comp;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev;
unsigned char *extra;
int extralen;
int enabled;
int streams; };

Описание полей:

desc Дескриптор для этой конечной точки, wMaxPacketSize с native порядком байт.

ss_ep_comp Дескриптор SuperSpeed-компаньона для этой конечной точки.

ssp_isoc_ep_comp Дескриптор SuperSpeedPlus isoc компаньона для этой конечной точки.

eusb2_isoc_ep_comp Дескриптор eUSB2 isoc компаньона для этой конечной точки.

urb_list Запросы URB, поставленные в очередь для этой конечной точки; обслуживается кодом usbcore.

hcpriv Для HCD; обычно хранит голову аппаратной очереди DMA (queue head, QH) с одним или большим количеством дескрипторов транзакций (transfer descriptors, TD) на один URB.

ep_dev ep_device для sysfs info.

extra Дескрипторы, следующие за этой конечной точкой в конфигурации.

extralen Сколько байт "extra" являются допустимыми.

enabled URB могут быть предоставлены для этой конечной точки.

streams Количество потоков USB-3, выделенных на конечной точке.

Описание:

USB-запросы всегда ставятся в очередь к заданной конечной точке, идентифицированной дескриптором в активном интерфейсе заданной конфигурации USB.

struct usb_interface

Через эту структуру драйверы работают с устройством USB.

Определение:

struct usb_interface {
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;
unsigned num_altsetting;
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; enum usb_interface_condition condition;
unsigned sysfs_files_created:1;
unsigned ep_devs_created:1;
unsigned unregistering:1;
unsigned needs_remote_wakeup:1;
unsigned needs_altsetting0:1;
unsigned needs_binding:1;
unsigned resetting_device:1;
unsigned authorized:1;
enum usb_wireless_status wireless_status;
struct work_struct wireless_status_work;
struct device dev;
struct device *usb_dev;
struct work_struct reset_ws; };

Описание полей:

altsetting Массив структур интерфейсов, по одной для каждой альтернативной настройки, которая может быть выбрана. Каждая из них включает набор конфигураций конечной точки. Определенный порядок структур не устанавливается.

cur_altsetting Текущая альтернативная настройка.

num_altsetting Количество определений альтернативных настроек.

intf_assoc Дескриптор связи интерфейса.

minor Младший (minor) номер, назначенный этому интерфейсу, если этот интерфейс привязан к драйверу, который использует номер USB major. Если этот интерфейс не использует USB major, то это поле не должно использоваться. Драйвер должен установить это значение в функции probe() драйвера после назначений minor-номера из USB core путем вызова usb_register_dev().

condition Состояние привязки (binding state) интерфейса: not bound, binding (в probe()), bound для драйвера, или unbinding (в disconnect()).

sysfs_files_created Существующие атрибуты sysfs.

ep_devs_created Существующие дочерние псевдо-устройства конечных точек (endpoint child pseudo-devices).

unregistering Флаг, устанавливаемый при отмене регистрации интерфейса.

needs_remote_wakeup Флаг, устанавливаемый когда драйвер требует возможности remote-wakeup во время autosuspend.

needs_altsetting0 Флаг, устанавливаемый когда был отложен (deferred) запрос set-interface для альтернативной настройки 0.

needs_binding Флаг, устанавливаемый когда драйвер должен быть повторно проверен (re-probed) или отключен (unbound) после операции reset или suspend, которую он не поддерживает.

resetting_device USB-ядро сбросило устройство, поэтому используйте альтернативную настройку 0 в качестве текущей; требуется распределение полосы пропускания после reset.

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

wireless_status Если устройство USB использует receiver/emitter combo, то показывает, подключен ли emitter.

wireless_status_work Используется для планирования изменений состояния беспроводной сети из атомарного контекста.

dev Представление модели драйвера этого устройства.

usb_dev Если интерфейс привязан к USB major, то это будет точка sysfs-представления для этого устройства.

reset_ws Используется для планируемых сбросов из атомарного контекста.

Описание:

Драйверы USB-устройств подключаются к интерфейсам физического устройства. Каждый интерфейс инкапсулирует одну высокоуровневую функцию, такую как передачу потока звука на динамик, или сообщение об изменении уровня громкости. У многих устройств USB есть только один интерфейс. Протокол, используемый для связи с конечными точками интерфейса, может быть определен спецификацией класса USB или его производителем (product vendor). Конечная точка управления (control endpoint, которую также называют конечной точкой по умолчанию, default endpoint) является частью каждого интерфейса, но она никогда не перечисляется дескрипторами интерфейса (поскольку подразумевается, что она всегда существует, и имеет номер 0).

Драйвер, привязанный к интерфейсу, может использовать стандартные вызовы модели драйвера, такие как dev_ get_drvdata() на элементе dev этой структуры.

Каждый интерфейс может иметь альтернативную настройку (alternate settings). Начальная конфигурация устройства устанавливает altsetting 0, но драйвер устройства впоследствии может поменять эту настройку, используя usb_set_interface(). Альтернативные настройки часто применяются для управления использованием периодических конечных точек (periodic endpoints), например когда различные конечные точки используют различную выделенный объем зарезервированной полосы пропускания шины (USB bandwidth). Все совместимые со стандартом устройства USB, использующие изохронные конечные точки, будут использовать их в нестандартных настройках.

В спецификации USB говорится, что номера альтернативных настроек должны иметь значения от 0 до значения на 1 меньше, чем общее количество альтернативных настроек. Но некоторым устройствам удается все испортить, и структуры не обязательно хранятся в числовом порядке. Используйте usb_altnum_to_altsetting() для поиска альтернативной настройки в массиве altsetting на основе его номера.

void usb_set_intfdata (struct usb_interface *intf, void *data)

Ассоциация с интерфейсом данных, специфичных для драйвера.

Параметры:

struct usb_interface *intf Структура интерфейса USB.

void *data Данные драйвера.

Описание:

Драйверы могут использовать эту функцию в своих callback-функциях probe() для привязки своих специфичных данных к интерфейсу.

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

struct usb_interface_cache

Долгосрочное (long-term) представление интерфейса устройства.

Определение:

struct usb_interface_cache {
unsigned num_altsetting;
struct kref ref;
struct usb_host_interface altsetting[]; };

Описание полей:

num_altsetting Количество определенных альтернативных настроек.

ref Счетчик ссылок.

altsetting Массив переменной длины структур интерфейса, по одному для каждой альтернативной настройки, которая может быть выбрана. Каждая включае набор конфигураций конечных точек. Они не будут в определенном порядке.

Описание:

Эти структуры сохраняются в течение всего времени жизни usb_device, в отличие от структуры usb_interface (которая сохраняется только пока установлена его конфигурация). К массиву altsetting можно обращаться через этих структуры в любое время, позволяя сравнивать конфигурации и обеспечивая поддержку псевдо-файла /sys/kernel/debug/usb/devices.

Пример содержимого файла /sys/kernel/debug/usb/devices:

$ sudo cat /sys/kernel/debug/usb/devices
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1 B: Alloc= 0/800 us ( 0%), #Int= 0, #Iso= 0 D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1d6b ProdID=0002 Rev= 6.14 S: Manufacturer=Linux 6.14.0-33-generic xhci-hcd S: Product=xHCI Host Controller S: SerialNumber=0000:00:0d.0 C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 4 Ivl=256ms
T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 7 D: Ver= 2.10 Cls=09(hub ) Sub=00 Prot=02 MxPS=64 #Cfgs= 1 P: Vendor=110a ProdID=0217 Rev= 6.23 S: Manufacturer=Microchip S: Product=USB2807 Hub C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=01 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=256ms I:* If#= 0 Alt= 1 #EPs= 1 Cls=09(hub ) Sub=00 Prot=02 Driver=hub E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=256ms
T: Bus=03 Lev=02 Prnt=02 Port=03 Cnt=01 Dev#= 18 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0403 ProdID=6001 Rev= 6.00 S: Manufacturer=FTDI S: Product=FT232R USB UART S: SerialNumber=B0029R1L C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr= 90mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=ftdi_sio E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms
T: Bus=03 Lev=01 Prnt=01 Port=06 Cnt=05 Dev#= 7 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=046d ProdID=c534 Rev=52.00 S: Manufacturer=Logitech S: Product=USB Receiver C:* #Ifs= 2 Cfg#= 1 Atr=a0 MxPwr= 98mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=01 Driver=usbhid E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=2ms I:* If#= 1 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=usbhid E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=2ms
T: Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=5000 MxCh= 7 D: Ver= 3.20 Cls=09(hub ) Sub=00 Prot=03 MxPS= 9 #Cfgs= 1 P: Vendor=110a ProdID=0217 Rev= 6.23 S: Manufacturer=Microchip S: Product=USB5807 Hub C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr= 0mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub E: Ad=81(I) Atr=13(Int.) MxPS= 2 Ivl=16ms
T: Bus=04 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#= 7 Spd=5000 MxCh= 0 D: Ver= 3.20 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 9 #Cfgs= 1 P: Vendor=0951 ProdID=1666 Rev= 1.10 S: Manufacturer=Kingston S: Product=DataTraveler 3.0 S: SerialNumber=4CEDFB74A3391711C91401E5 C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=144mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=81(I) Atr=02(Bulk) MxPS=1024 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS=1024 Ivl=0ms

Файл /sys/kernel/debug/usb/devices используется для получения детализированной информации о USB-шинах и подключенных устройствах в Linux. Это текстовое представление данных, которыми ядро оперирует для управления USB.

[Формат и интерпретация файла]

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

Вот расшифровка основных меток:

Метка Описание Пример и значение
T: Топология устройства Bus=01 Lev=01 Prnt=01 Port=03 Cnt=01 Dev#= 3 Spd=1.5 MxCh=0
• Bus = номер шины
• Lev = уровень в дереве устройств (00 для корневого хаба)
• Spd = скорость (1.5, 12, 480 Мбит/с)
D: Дескриптор устройства Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
• Ver = версия USB
• Cls = класс устройства
• #Cfgs = количество конфигураций
P: Идентификаторы производителя (Vendor/Product, VID/PID) Vendor=045e ProdID=082c Rev=01.00
• Vendor = ID производителя (VID)
• ProdID = ID продукта (PID)
S: Строки (String Descriptors) S: Manufacturer=Microsoft
S: Product=Microsoft Ergonomic Keyboard
Читаемые строки, возвращаемые устройством.
C: Дескриптор конфигурации (Configuration Descriptor) #Ifs= 2 Cfg#= 1 Atr=a0 MxPwr=100mA
• #Ifs = количество интерфейсов
• Atr = атрибуты (e.g., питание)
• MxPwr = максимальное потребление
I: Дескриптор интерфейса (Interface Descriptor) If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=01 Driver=usbhid
• Cls = класс интерфейса
• Driver = используемый драйвер ядра
E: Дескриптор конечной точки (Endpoint Descriptor) Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=10ms
• Ad = адрес и направление (I=In, O=Out)
• Atr = тип передачи (Int., Bulk, Isochronous)

[Практическое использование]

Для начала работы с файлом может потребоваться смонтированная debugfs и загружен модуль usbmon.

# Монтирование debugfs (требует прав root)
sudo mount -t debugfs none /sys/kernel/debug

# Если директории usbmon нет, загрузите модуль
sudo modprobe usbmon

После этого можно просматривать и анализировать файл.

Просмотр всех устройств:

sudo cat /sys/kernel/debug/usb/devices

Поиск конкретного устройства по VID и PID:

sudo cat /sys/kernel/debug/usb/devices | grep -A 10 -B 2 "Vendor=046d"

[Назначение и альтернативы]

Альтернативы: для большинства задач удобнее использовать утилиты lsusb или скрипт usb-devices, которые представляют информацию в более удобном формате.

Файл /sys/kernel/debug/usb/devices — это низкоуровневый инструмент отладки. Он полезен для глубокого анализа и решения сложных проблем с драйверами или конфигурацией устройств.

Для повседневных задач проверки подключенных устройств чаще используются:

- lsusb — быстрый просмотр дерева устройств
- usb-devices — тот же источник данных, но с фильтрацией только активных интерфейсов

struct usb_host_config

Представление конфигурации утройства.

Описание:

struct usb_host_config {
struct usb_config_descriptor desc;
char *string;
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
struct usb_interface *interface[USB_MAXINTERFACES];
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra;
int extralen; };

Описание полей:

desc Дескриптор конфигурации устройства.

string Указатель на кешированноую версию строки iConfiguration, если она присутствует для этой конфигурации.

intf_assoc Список любых дескрипторов ассоциации интерфейсов в этом конфигурационном элементе.

interface Массив указателей на структуры usb_interface, по одному на каждый интерфейс в конфигурации. Количество интерфейсов сохранено в desc.bNumInterfaces. Это указатели достоверны только пока конфигурация активна.

intf_cache Массив указателей на структуры usb_interface_cache, по одному на каждый интерфейс в конфигурации. Эти структуры существуют в течение всего времени жизни устройства.

extra Указатель на буфер, содержащий все дополнительные (extra) дескрипторы, связанные с этой конфигурацией (предшествующие первому дескриптору интерфейса).

extralen Длина буфера extra дескрипторов.

Описание:

Устройства USB могут иметь несколько конфигураций, но в любой момент времени может быть активной только одна из них. Каждая конфигурация инкапсулирует различное рабочее окружение; например, устройства dual-speed будет иметь раздельные конфигурации для операций full-speed и high-speed. Количество доступных конфигуарций сохраняется в дескрипторе устройства как bNumConfigurations.

Конфигурация может содержать несколько интерфейсов. Каждый из них соответствует отличающейся функции устройства USB, и все они доступны, пока конфигурация активна. Стандарт USB говорит, что подразумевается нумерация интерфейсов от 0 до desc.bNumInterfaces-1, однако некоторые устройства в этом отношении ведут себя неправильно. Кроме того, массив interface не гарантированно может быть отсортирован в порядке номеров. Используйте usb_ifnum_to_if() чтобы найти запись интерфейса по его номеру.

Драйверы устройств не должны пытаться активировать конфигурации. Выбор конфигурации для установки это решение политики, основанное на таких соображениях, как доступная мощность, предоставленная функциональность, и желание пользователя (выраженное через инструменты userspace). Однако драйверы могут вызвать usb_reset_configuration() для повторной инициализации текущей конфигурации и все её интерфейсов.

struct usb_device

Представление в ядре устройства USB.

Определение:

struct usb_device {
int devnum;
char devpath[16];
u32 route;
enum usb_device_state state;
enum usb_device_speed speed;
unsigned int rx_lanes;
unsigned int tx_lanes;
enum usb_ssp_rate ssp_rate;
struct usb_tt *tt;
int ttport;
unsigned int toggle[2];
struct usb_device *parent;
struct usb_bus *bus;
struct usb_host_endpoint ep0;
struct device dev;
struct usb_device_descriptor descriptor;
struct usb_host_bos *bos;
struct usb_host_config *config;
struct usb_host_config *actconfig;
struct usb_host_endpoint *ep_in[16];
struct usb_host_endpoint *ep_out[16];
char **rawdescriptors;
unsigned short bus_mA;
u8 portnum;
u8 level;
u8 devaddr;
unsigned can_submit:1;
unsigned persist_enabled:1;
unsigned reset_in_progress:1;
unsigned have_langid:1;
unsigned authorized:1;
unsigned authenticated:1;
unsigned lpm_capable:1;
unsigned lpm_devinit_allow:1;
unsigned usb2_hw_lpm_capable:1;
unsigned usb2_hw_lpm_besl_capable:1;
unsigned usb2_hw_lpm_enabled:1;
unsigned usb2_hw_lpm_allowed:1;
unsigned usb3_lpm_u1_enabled:1;
unsigned usb3_lpm_u2_enabled:1;
int string_langid;
char *product;
char *manufacturer;
char *serial;
struct list_head filelist;
int maxchild;
u32 quirks;
atomic_t urbnum;
unsigned long active_duration;
unsigned long connect_time;
unsigned do_remote_wakeup:1;
unsigned reset_resume:1;
unsigned port_is_suspended:1;
unsigned offload_at_suspend:1;
int offload_usage;
enum usb_link_tunnel_mode tunnel_mode;
struct device_link *usb4_link;
int slot_id;
struct usb2_lpm_parameters l1_params;
struct usb3_lpm_parameters u1_params;
struct usb3_lpm_parameters u2_params;
unsigned lpm_disable_count;
u16 hub_delay;
unsigned use_generic_driver:1; };

Описание полей:

devnum Номер устройства; адрес на шине USB.

devpath Строка идентификатора устройства (device ID) для использования в сообщениях (например /port/...)

route hex-строка топологии дерева для использования вместе с xHCI.

state Состояние устройства: configured, not attached и т. п.

speed Скорость устройства: high/full/low (или error).

rx_lanes Количество используемых полос приема (rx lanes), стандарт USB 3.2 добавил поддержку dual-lane.

tx_lanes Количество используемых полос передачи (tx lanes), стандарт USB 3.2 добавил поддержку dual-lane.

ssp_rate Скорость физической сигнализации SSP (SuperSpeed Plus phy signaling rate) и количество полос (lane count).

tt Информация Transaction Translator; используется с low/full speed dev, highspeed hub.

ttport Порт устройства на этом хабе tt.

toggle Один бит для каждой endpoint, с конечными точками ([0] = IN, [1] = OUT)

parent Наш хаб, если только мы не корень.

bus Шина, частью которой мы являемся.

ep0 Данные конечной точки управления (endpoint 0, default control pipe).

dev Общий интерфейс устройства (generic device interface).

descriptor Дескриптор устройства USB.

bos Набор дескрипторов BOS устройства USB.

Поле bos в структуре struct usb_device предназначено для хранения BOS (Binary Device Object Store) Descriptor Set — это специальный набор дескрипторов, который появился в спецификации USB 3.0 и предоставляет расширенные возможности устройства.

[Что такое BOS Descriptor]

BOS это корневой дескриптор, который содержит информацию о дополнительных возможностях USB-устройства, особенно связанных с:

- USB 3.0 и выше функции
- SuperSpeed возможности
- Расширенные возможности устройства
- Поддержка новых спецификаций

Структура BOS в ядре Linux:

struct usb_bos_descriptor {
__u8 bLength;
__u8 bDescriptorType; // = USB_DT_BOS
__le16 wTotalLength; // Общий размер всех дескрипторов BOS
__u8 bNumDeviceCaps; // Количество Device Capability Descriptors
// Далее следуют Device Capability Descriptors... } __attribute__((packed));

[Device Capability Descriptors]

BOS содержит несколько типов дескрипторов возможностей:

1. Wireless USB

struct usb_wireless_cap_descriptor {
__u8 bLength;
__u8 bDescriptorType; // = USB_DT_DEVICE_CAPABILITY
__u8 bDevCapabilityType; // = USB_CAP_TYPE_WIRELESS_USB
// Параметры беспроводного USB };

2. SuperSpeed USB

struct usb_ss_cap_descriptor {
__u8 bLength;
__u8 bDescriptorType; // = USB_DT_DEVICE_CAPABILITY
__u8 bDevCapabilityType; // = USB_CAP_TYPE_SS
__u8 bmAttributes; // характеристики LTM (Latency Tolerance Messaging)
__le16 wSpeedSupported; // поддерживаемые скорости
__u8 bFunctionalitySupport;// минимальная функциональность
__u8 bU1devExitLat; // задержка выхода из U1
__le16 bU2devExitLat; // задержка выхода из U2 };

3. Container ID

struct usb_container_id_descriptor {
__u8 bLength;
__u8 bDescriptorType; // = USB_DT_DEVICE_CAPABILITY
__u8 bDevCapabilityType; // = USB_CAP_TYPE_CONTAINER_ID
__u8 bReserved;
__u8 ContainerID[16]; // 128-битный уникальный идентификатор };

4. Platform Descriptor

struct usb_platform_descriptor {
__u8 bLength;
__u8 bDescriptorType; // = USB_DT_DEVICE_CAPABILITY
__u8 bDevCapabilityType; // = USB_CAP_TYPE_PLATFORM
__u8 bReserved;
__u8 PlatformCapabilityUUID[16]; // UUID специфичной платформы
// Далее следуют специфичные данные... };

[Практическое использование]

В процессе перечисления устройства:

// Когда устройство подключается
struct usb_device *udev;

// Чтение BOS дескриптора usb_get_bos_descriptor(udev);

// Использование информации из BOS
if (udev->bos) {
struct usb_ss_cap_descriptor *ss_cap;

// Получение SuperSpeed capability
ss_cap = usb_get_ss_cap_descriptor(udev);
if (ss_cap) {
// Настройка контроллера на основе возможностей устройства
configure_superspeed_params(udev, ss_cap);
} }

Пример из реального устройства:

BOS Descriptor:
  Total Length: 37
  Num Device Capabilities: 3
  
  SuperSpeed USB Device Capability:
    Attributes: 0x0e
    Supported Speeds: SuperSpeed
    Functionality Support: Lowest speed is SuperSpeed
    U1 Device Exit Latency: 10 microseconds
    U2 Device Exit Latency: 2047 microseconds
  
  Container ID Device Capability:
    Container ID: {12345678-1234-1234-1234-123456789abc}
  
  Platform Descriptor:
    Platform Capability UUID: Microsoft OS 2.0

[Для чего нужно поле bos]

1. Определение возможностей устройства

- Поддержка SuperSpeed и выше
- Энергосберегающие режимы (U1/U2)
- Беспроводные возможности

2. Управление питанием

// Использование информации о задержках для управления питанием
if (udev->bos && udev->bos->ss_cap) {
u16 u2_exit_lat = udev->bos->ss_cap->bU2devExitLat;
schedule_power_management(udev, u2_exit_lat); }

3. Уникальная идентификация

// Получение Container ID для отслеживания устройства
if (udev->bos && udev->bos->ext_cap) {
uuid_t *container_id = &udev->bos->ext_cap->ContainerID;
track_device_across_ports(container_id); }

4. Поддержка платформо-специфичных функций

// Проверка поддержки специфичных для Windows фич
if (udev->bos && udev->bos->ssp_cap) {
handle_platform_specific_capabilities(udev); }

[Значение для разработчиков]

Понимание BOS важно для:

- Разработчиков драйверов — правильная обработка расширенных возможностей
- Системных интеграторов — оптимизация производительности USB 3.0+ устройств
- Отладки проблем — анализ возможностей неправильно работающих устройств

BOS descriptor set — ключевой механизм для поддержки современных USB-функций, обеспечивающий обратную совместимость и расширяемость стандарта USB.

config Все конфигурации устройства.

actconfig Активная конфигурация.

ep_in Массив конечных точек IN.

ep_out Массив конечных точек OUT.

rawdescriptors Сырые дескрипторы для каждого config.

bus_mA Ток, доступный из этой шины.

portnum Номер порта родителя (origin 1).

level Количество предков USB hub.

devaddr Адрес устройства, XHCI: назначается аппаратно, другие: такой же, что и devnum.

can_submit Могут быть предоставлены URB.

persist_enabled Для этого устройства разрешено USB_PERSIST.

reset_in_progress Это устройство находится в состоянии сброса.

have_langid Достоверен ли string_langid.

authorized Политика сказала, что мы можем это использовать; (user space) политика определяет, авторизованы ли мы для использования этого устройства. По умолчанию проводные устройства USB авторизованы. Устройства WUSB не авторизованы, пока мы их не авторизуем из user space (эта документация требует дополнения).

authenticated Пройдена crypto-аутентификация.

lpm_capable Устройство поддерживает LPM.

Поле lpm_capable в структуре usb_device указывает на поддержку устройством LPM (Link Power Management) — механизма управления энергопотреблением на уровне соединения USB.

[Что такое LPM (Link Power Management)]

LPM это протокол энергосбережения в USB, который позволяет:

- Приостанавливать соединение между хостом и устройством
- Значительно снижать энергопотребление в периоды неактивности
- Быстро восстанавливать соединение при необходимости передачи данных

[Типы LPM]

USB 2.0 LPM (Link Power Management)

// Для USB 2.0 устройств
struct usb2_lpm_parameters {
unsigned int besl; // Best Effort Service Latency
bool lpm_capable; // Устройство поддерживает LPM
bool lpm_enabled; // LPM включено для этого устройства };

USB 3.0 LPM (U1/U2 States)

// Для USB 3.0+ устройств (SuperSpeed)
struct usb3_lpm_parameters {
unsigned int mel; // Maximum Exit Latency
bool lpm_capable; // Поддержка U1/U2 states
bool lpm_enabled; // U1/U2 включены
bool hub_delay; // Задержка хаба };

Структура в ядре Linux:

struct usb_device {
// ... другие поля
struct usb2_lpm_parameters l1_params; // USB 2.0 LPM параметры
struct usb3_lpm_parameters u1_params; // USB 3.0 U1 state
struct usb3_lpm_parameters u2_params; // USB 3.0 U2 state
unsigned lpm_capable:1; // Общая поддержка LPM
// ... другие поля };

Состояния LPM:

USB 2.0 L1 State

- Промежуточное состояние между L0 (активный) и L2/L3 (suspend)
- Тактовая синхронизация сохраняется
- Быстрое восстановление ~10-100 микросекунд
- Энергопотребление снижено значительно

USB 3.0 U1/U2 States

U1 State (легкий сон)

- Восстановление: ~10 микросекунд
- Энергосбережение: умеренное

U2 State (глубокий сон)

- Восстановление: ~100-500 микросекунд
- Энергосбережение: максимальное

[Как работает LPM]

Процесс перехода в LPM:

1. Обнаружение неактивности — хост определяет, что данные не передаются
2. Проверка возможности — хост проверяет lpm_capable и параметры устройства
3. Отправка команды — хост отправляет LPM token устройству
4. Подтверждение — устройство подтверждает переход в sleep state
5. Снижение питания — физический линк переходит в low-power mode

Процесс выхода из LPM:

1. Обнаружение активности — хост или устройство инициируют восстановление
2. Выход из sleep state — линк активируется
3. Ресинхронизация — восстановление тактовой синхронизации
4. Возобновление работы — нормальная передача данных

[Практическое использование]

В драйвере хоста:

// Проверка поддержки LPM перед настройкой
if (udev->lpm_capable) {
// Настройка параметров LPM на основе возможностей устройства
err = usb_set_lpm_parameters(udev);
if (!err) {
// Включение LPM для этого устройства
usb_enable_lpm(udev);
} }

В Device Tree:

usb_controller: usb@12340000 {
compatible = "vendor,usb-host";
reg = < 0x12340000 0x1000>;

/* Параметры LPM */
usb-lpm-capable;
lpm-besl = < 5>; // Best Effort Service Latency
lpm-timeout = < 200>; // Таймаут перехода в LPM (мс) };

Пример параметров LPM:

// Типичные значения для USB устройства
struct usb_device udev = {
.lpm_capable = 1, // Устройство поддерживает LPM

.l1_params = {
.besl = 4, // Best Effort Service Latency
.lpm_capable = 1, // Поддержка USB 2.0 LPM
.lpm_enabled = 1, // LPM включено
},

.u1_params = {
.mel = 10, // Maximum Exit Latency = 10 мкс
.lpm_capable = 1, // Поддержка U1 state
.lpm_enabled = 1, // U1 включен
},

.u2_params = {
.mel = 200, // Maximum Exit Latency = 200 мкс
.lpm_capable = 1, // Поддержка U2 state
.lpm_enabled = 1, // U2 включен
} };

[Преимущества LPM]

Для мобильных устройств:

- Увеличение времени работы от батареи
- Снижение тепловыделения в режиме ожидания
- Улучшенная пользовательский опыт

Для серверов и ПК:

- Снижение общего энергопотребления
- Улучшенное управление питанием множества USB устройств
- Соответствие стандартам энергоэффективности

[Значение для разработчиков]

Поле lpm_capable важно для:

- Оптимизации энергопотребления — автоматическое использование LPM где возможно
- Обработки ошибок — правильная реакция на устройства без поддержки LPM
- Отладки проблем — диагностика нестабильности при использовании LPM
- Разработки драйверов — правильная инициализация параметров питания

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

lpm_devinit_allow Позволяет LPM, инициированный устройством USB3, задержка выхода в пределах диапазона.

usb2_hw_lpm_capable Устройство может выполнять USB2 hardware LPM.

usb2_hw_lpm_besl_capable Устройство может выполнять USB2 hardware BESL LPM.

usb2_hw_lpm_enabled Разрешен USB2 hardware LPM.

usb2_hw_lpm_allowed Userspace позволяет разрешить USB 2.0 LPM.

usb3_lpm_u1_enabled Разрешен USB3 hardware U1 LPM.

usb3_lpm_u2_enabled Разрешен USB3 hardware U2 LPM.

string_langid Идентификатор языка (language ID) для строк.

product Строка идентификатора продукта iProduct, если она имеется (static).

manufacturer Строка идентификатора производителя iManufacturer, если она имеется (static).

serial Строка iSerialNumber, если она имеется (static).

filelist Файлы usbfs, открытые на этом устройстве.

maxchild Количество портов, если хаб.

quirks Причуды всего устройства.

Поле quirks в структуре usb_device предназначено для хранения флагов, которые указывают на особое поведение или "особенности" (quirks) USB-устройства, требующие специальной обработки в драйвере или на уровне USB-подсистемы.

[Что такое USB Quirks]

USB Quirks это механизм обхода проблем, связанных с:

- Нестандартным поведением конкретных устройств
- Ошибками в реализации USB спецификации производителями
- Проблемы совместимости между устройствами и хост-контроллерами
- Баг-в-баг совместимость (методы обхода для известных проблем)

Структура и флаги:

struct usb_device {
// ... другие поля
unsigned quirks:1; // Общий флаг наличия quirks
unsigned int quirks; // Битовая маска специфичных quirks
// ... другие поля };

[Основные типы Quirks]

1. Проблемы с управлением питанием

// Устройство неправильно реагирует на команды suspEND/resume
#define USB_QUIRK_RESET_RESUME (1 < < 0)
// Требуется полный сброс при resume
#define USB_QUIRK_RESET_MORPHS (1 < < 1)
// Игнорировать запросы на отключение устройства
#define USB_QUIRK_NO_DISCONNECT_SUSPEND (1 < < 2)

2. Проблемы с дескрипторами

// Дескрипторы устройства повреждены или неверны
#define USB_QUIRK_IGNORE_DESCRIPTOR (1 < < 3)
// Использовать сырые дескрипторы вместо парсинга
#define USB_QUIRK_DESC_RAW (1 < < 4)
// Дескрипторы устройства меняются после инициализации
#define USB_QUIRK_DESC_MUTABLE (1 < < 5)

3. Проблемы с контролем соединения

// Устройство теряет соединение при suspend
#define USB_QUIRK_DISCONNECT_SUSPEND (1 < < 6)
// Требуется задержка после сброса устройства
#define USB_QUIRK_DELAY_INIT (1 < < 7)
// Устройство не отвечает на контроль соединения
#define USB_QUIRK_HUB_NO_CONNECT (1 < < 8)

4. Проблемы с передачей данных

// Устройство не поддерживает контроль конечных точек
#define USB_QUIRK_NO_ENDPOINT_HALT (1 < < 9)
// Проблемы с обработкой коротких пакетов
#define USB_QUIRK_SHORT_PACKET (1 < < 10)
// Устройство требует специфичной обработки URB
#define USB_QUIRK_URB_SPECIAL (1 < < 11)

[Как устанавливаются Quirks]

1. Статически в коде ядра

// В драйверах устройств или USB core
static const struct usb_device_id quirks_table[] = {
// Устройства Logitech требуют reset-resume
{ USB_DEVICE(0x046d, 0xc52b), .driver_info = USB_QUIRK_RESET_RESUME },
// Устройства Creative имеют поврежденные дескрипторы
{ USB_DEVICE(0x041e, 0x4064), .driver_info = USB_QUIRK_IGNORE_DESCRIPTOR },
{ } };

2. Через модульные параметры

# При загрузке модуля ядра
sudo modprobe usbcore quirks=1234:5678:q,046d:c52b:r

# Где:
# q = USB_QUIRK_IGNORE_DESCRIPTOR
# r = USB_QUIRK_RESET_RESUME

3. Внутри драйвера устройства

// Во время probe функции
static int my_usb_driver_probe(struct usb_interface *intf,
const struct usb_device_id *id) {
struct usb_device *udev = interface_to_usbdev(intf);

// Установка quirks для конкретного устройства
udev->quirks |= USB_QUIRK_RESET_RESUME;
udev->quirks |= USB_QUIRK_NO_ENDPOINT_HALT;

return 0; }

[Практические примеры]

Пример 1: устройство с проблемами suspend/resume

// Мышь Logitech, которая не просыпается после suspend
{ USB_DEVICE(0x046d, 0xc52b), .driver_info = 
USB_QUIRK_RESET_RESUME | USB_QUIRK_DISCONNECT_SUSPEND }

Обработка в коде:

if (udev->quirks & USB_QUIRK_RESET_RESUME) {
// Вместо стандартного resume выполняем полный reset
usb_reset_device(udev); } else {
// Стандартная процедура resume
usb_port_resume(udev); }

Пример 2: устройство с поврежденными дескрипторами

// Веб-камера с неверными дескрипторами
{ USB_DEVICE(0x041e, 0x4064), .driver_info = 
USB_QUIRK_IGNORE_DESCRIPTOR | USB_QUIRK_DESC_RAW }

Обработка в коде:

if (udev->quirks & USB_QUIRK_IGNORE_DESCRIPTOR) {
// Используем заранее известные параметры вместо дескрипторов
setup_device_with_hardcoded_params(udev); } else {
// Стандартный парсинг дескрипторов
parse_usb_descriptors(udev); }

Пример 3: устройство с проблемами передачи данных

// Сетевая карта с проблемами контроля конечных точек
{ USB_DEVICE(0x0b95, 0x7720), .driver_info = 
USB_QUIRK_NO_ENDPOINT_HALT }

Обработка в коде:

// В функции очистки конечной точки
if (!(udev->quirks & USB_QUIRK_NO_ENDPOINT_HALT)) {
// Стандартная очистка конечной точки
usb_clear_halt(udev, pipe); } else {
// Альтернативный метод для проблемных устройств
usb_reset_endpoint(udev, epaddr); }

[Где просмотреть активные Quirks]

Через sysfs:

# Просмотр информации об USB устройстве
cat /sys/kernel/debug/usb/devices

# В выводе будут указаны активные quirks
# T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 0
# D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
# P:  Vendor=1234 ProdID=5678 Rev=01.00
# S:  Manufacturer=Problematic_Device
# C:  #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA
# I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=usb
# Q:  Quirks=0x00000005  # RESET_RESUME | IGNORE_DESCRIPTOR

Через dmesg:

# Ядро сообщает о примененных quirks при подключении устройства
dmesg | grep -i quirk
# [   12.345678] usb 1-1.2: New USB device found, idVendor=046d, idProduct=c52b
# [   12.345679] usb 1-1.2: Applied USB_QUIRK_RESET_RESUME

[Значение для системы]

Поле quirks критически важно для:

- Совместимости со множеством нестандартных USB устройств
- Стабильности системы — предотвращение сбоев проблемных устройств
- Обратной совместимости со старыми устройствами
- Гибкости — возможность добавлять workaround'ы без изменения драйверов

Этот механизм позволяет ядру Linux работать с тысячами различных USB устройств, многие из которых имеют уникальные "особенности" реализации USB стандарта.

urbnum Количество URB-ов предоставленных для всего устройства.

active_duration Общее время, в течение которого устройство не было приостановлено.

connect_time Время первого подключения устройства.

do_remote_wakeup Фича remote wakeup должна быть разрешена.

reset_resume Необходим reset вместо resume.

port_is_suspended Порт в восходящем (upstream) направлении приостановлен (L2 или U3).

offload_at_suspend Разгрузка действий (offload activities) во время приостановки (suspend) разрешена.

offload_usage Количество offload-активностей, произошедших на этом устройстве USB.

tunnel_mode Соединение традиционное (native) или туннель через USB4.

usb4_link Соединение устройства с интерфейсом хоста USB4.

slot_id Slot ID, назначенный xHCI.

l1_params Задержка лучшего обслуживания (best effort service latency) для состояния USB2 L1 LPM, и таймаут L1.

u1_params Задержки выхода (exit latencies) для состояния USB3 U1 LPM и таймаут, инициированный хабом.

u2_params Задержки выхода (exit latencies) для состояния USB3 U2 LPM и таймаут, инициированный хабом.

lpm_disable_count Счетчик ссылок (Ref count), используемый usb_disable_lpm() и usb_enable_lpm() для отслеживания количества функций, которые требуют запрещения USB 3.0 Link Power Management для этого usb_device. Этот счетчик должен изменяться только этими функциями, при этом удерживается bandwidth_mutex.

hub_delay Кешированная задержка, состоящая из: parent->hub_delay + wHubDelay + tTPTransmissionDelay (40 нс). Будет использоваться как wValue для запросов SetIsochDelay.

use_generic_driver Запрашивает ядро драйвера повторно использовать общий (generic) драйвер.

Замечания: драйвера usbcore не должны напрямую устанавливать usbdev->state. Вместо этого используется usb_set_device_state().

usb_hub_for_each_child (hdev, port1, child)

Итерация по всем дочерним устройствам хаба.

Параметры:

hdev Устройство USB, принадлежащее хабу.

port1 Номер порта, связанного с этим устройством.

child Указатель на дочернее устройство.

int usb_interface_claimed (struct usb_interface *iface)

Возвратит true, если интерфейс привязан (claimed).

Параметры:

struct usb_interface *iface Проверяемый интерфейс.

Описание:

Возвратит true (ненулевое значение), если интерфейс claimed, иначе false (ноль). "Interface is claimed" означает, что интерфейс USB устройства "заявлен" (привязан) к конкретному драйверу, который теперь отвечает за управление этим интерфейсом. Привязка обеспечивает эксклюзивный доступ к интерфейсу, предотвращение конфликтов доступа между драйверами и четкое разделение ответственности за функционал.

Замечание: вызывающий код должен завладеть блокировкой чтения модели шины (model usb bus readlock). Поэтому записи probe() драйвера не требуют дополнительной блокировки, но вызовы других контекстов могут потребовать исключительного владения этой блокировкой.

int usb_make_path (struct usb_device *dev, char *buf, size_t size)

Возвратит стабильный путь устройства в дереве USB.

Параметры:

struct usb_device *dev Устройство, для которого составляется путь.

char *buf Куда будет помещена строка пути.

size_t size Размер буфера строки buf.

Возвращаемое значение: длина строки (> 0) или отрицательное число, если буфер слишком мал.

Замечание: этот идентификатор должен быть "стабильным" (stable), отражая физические пути в аппаратуре, такие как адреса физической шины для host-контроллеров или порты на хабе USB. Это делает такие пути постоянными до тех пор, пока система не была физически переконфигурирована путем переключения кабелей дерева устройств USB или перемещения host-контроллеров USB. Добавление и удаление устройств, включая виртуальные корневые хабы (virtual root hubs) в модулях драйвера host-контроллера, не меняют эти идентификаторы пути; на них не действуют ни перезагрузки, ни переэнумерация устройств USB. Это более полезные идентификаторы, чем изменяемые ("unstable"), такие как номера шин или адреса устройств.

За частичным исключением для устройств, подключенных к корневым хабам USB 2.0, эти идентификаторы тоже предсказуемы. Пока дерево устройств не изменено, подключение любого USB-устройства к данному порту концентратора всегда дает ему один и тот же путь. Из-за использования companion контроллеров устройства, подключенные к портам на корневых хабах USB 2.0 (хост-контроллеры EHCI), получат один идентификатор пути, если они работают на high скорости, и другой, если они работают на full или low скорости.

USB_DEVICE (vend, prod)

Макрос, используемый для описания определенного устройства USB.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

Описание:

Этот макрос используется для создания структуры usb_device_id, соответствующей определенному устройству.

USB_DEVICE_VER (vend, prod, lo, hi)

Описывает определенное устройство USB с диапазоном версии.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

lo Значение bcdDevice_lo.

hi Значение bcdDevice_hi.

Описание:

Этот макрос используется для создания структуры usb_device_id, соответствующей определенному устройству с диапазоном версии.

USB_DEVICE_INTERFACE_CLASS (vend, prod, cl)

Описывает устройство USB с определенным классом интерфейса.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

cl Значение bInterfaceClass.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному классу интерфейса устройств.

USB_DEVICE_INTERFACE_PROTOCOL (vend, prod, pr)

Описывает устройство USB со специфическим протоколом интерфейса.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

pr Значение bInterfaceProtocol.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному протоколу интерфейса устройств.

USB_DEVICE_INTERFACE_NUMBER (vend, prod, num)

Описывает устройство USB с определенным номером интерфейса.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

num Значение bInterfaceNumber.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному номеру интерфейса устройств.

USB_DEVICE_INFO (cl, sc, pr)

Макрос, используемый для описания класса устройств USB/

Параметры:

cl Значение bDeviceClass.

sc Значение bDeviceSubClass.

pr Значение bDeviceProtocol.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному классу устройств.

USB_INTERFACE_INFO (cl, sc, pr)

Макрос, используемый для описания класса интерфейсов USB.

Параметры:

cl Значение bDeviceClass.

sc Значение bDeviceSubClass.

pr Значение bDeviceProtocol.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному классу интерфейсов.

USB_DEVICE_AND_INTERFACE_INFO (vend, prod, cl, sc, pr)

Описывает определенное устройство USB с классом интерфейсов USB.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

prod 16-разрядный идентификатор продукта (USB Product ID, PID).

cl Значение bInterfaceClass.

sc Значение bInterfaceSubClass.

pr Значение bInterfaceProtocol.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному устройству с определенным классом интерфейсов. Он особенно полезен при явном сопоставлении устройств, которые имеют определенные значения bDeviceClass, но также имеют совместимые со стандартом интерфейсы.

USB_VENDOR_AND_INTERFACE_INFO (vend, cl, sc, pr)

Описывает определенного производителя (USB vendor) с классом интерфейсов USB.

Параметры:

vend 16-разрядный идентификатор производителя (USB Vendor ID, VID).

cl Значение bInterfaceClass.

sc Значение bInterfaceSubClass.

pr Значение bInterfaceProtocol.

Описание:

Этот макрос используется для создания структуры usb_device_id, которая соответствует определенному вендору с определенным классом интерфейсов. Он особенно полезен при явном сопоставлении устройств, которые имеют определенные значения специфичного для производителя bDeviceClass, но также имеют совместимые со стандартом интерфейсы.

struct usb_driver

Идентифицирует для usbcore драйвер интерфейса USB.

Определение:

struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
void (*shutdown)(struct usb_interface *intf);
const struct usb_device_id *id_table;
const struct attribute_group **dev_groups;
struct usb_dynids dynids;
struct device_driver driver;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int disable_hub_initiated_lpm:1;
unsigned int soft_unbind:1; };

Описание полей:

name Имя драйвера, которое должно быть уникальным среди драйверов USB, и должно нормально совпадать с именем модуля.

probe Вызывается чтобы узнать, готов ли драйвер управлять определенным интерфейсом на устройстве. Если это так, то probe возвратит 0 и использует usb_set_intfdata() для ассоциации с интерфейсом специфичных для драйвера данных. Она также может использовать usb_set_interface() для указания подходящей альтернативной настройки (altsetting). Если драйвер не хочет управлять интерфейсом, то вернет -ENODEV, если произошли чисто ошибки IO, то соответствующее отрицательное значение errno.

disconnect Вызывается, когда интерфейс больше недоступен, обычно из-за отключения устройства или выгрузки модуля драйвер.

unlocked_ioctl Используется для драйверов, которые хотят общаться с userspace через файловую систему "usbfs". Это позволяет устройствам предоставить пути выставлять информацию для user space независимо от того, где они находятся (или не находятся) в файловой системе.

suspend Вызывается, когда устройство переходит к приостановке системой либо из-за system sleep, либо из-за контекста runtime suspend. Возвращаемое значение будет игнорироваться в контексте system sleep, так что НЕ ПЫТАЙТЕСЬ продолжить использовать устройство, если в этом случае приостановка была неудачной. Вместо этого позвольте подпрограмме возобновления (resume или reset-resume) выполнить восстановление из ошибки.

resume Вызывается, когда работа устройства возобновляется системой.

reset_resume Вызывается, когда приостановленное устройство сбрасывается (reset) вместо возобновления (resume).

pre_reset Вызывается из usb_reset_device(), когда что-либо собирается сбросить устройство. Эта подпрограмма не должна выполнить возврат, пока драйвер не будет иметь активных URB для устройства, и никакие другие URB не могут быть предоставлены до вызова метода post_reset.

post_reset Вызывается из usb_reset_device() после того, как устройство было сброшено.

shutdown Вызывается во время выключения (shut-down) для приостановки устройства.

id_table Драйвера USB используют таблицу идентификаторов для поддержки горячего подключения (hotplugging). Экспортируйте это с MODULE_DEVICE_TABLE(usb,...). Это должно быть установлено, или функция probe вашего драйвера никогда не будет вызвана.

dev_groups Атрибуты, подключенные к устройству, которые будут созданы однократно после привязки к драйверу.

dynids Используется внутренне для хранения списка динамически добавленных идентификаторов устройств для этого драйвера.

driver Базовая структура модели драйвера.

no_dynamic_id Если установлено в 1, то USB core не позволит добавлять динамические идентификаторы для этого драйвера, не давая создавать файл sysfs.

supports_autosuspend Если установлено в 0, то USB не позволяет автоматическую приостановку (autosuspend) для интерфейсов, привязанных к этому драйверу.

disable_hub_initiated_lpm Если установлено в 1, то USB core не позволит хабам инициировать переходы состояния lower power link, когда произойдет таймаут ожидания (idle timeout). Инициированный устройством USB 3.0 link PM все еще будет разрешен.

soft_unbind Если установлено в 1, то USB core не будет прибивать URB-ы и запрещать конечные точки перед вызовом метода disconnect драйвера.

Описание:

Драйверы интерфейса USB должны предоставить name, методы probe() и disconnect(), а также id_table. Другие поля драйвера опциональны.

Таблица идентификаторов id_table используется в горячем подключении устройств (hotplugging). Она хранит набор дескрипторов и специальные данные, которые могут быть связаны с каждой записью. Эта таблица используется поддержки горячего подключения как режимом пользователя (user mode), так и режимом ядра (kernel mode).

Методы probe() и disconnect() вызываются в контексте, где они могут засыпать, но они должны избегать злоупотребления привилегиями. Большая часть работы по подключению к устройству должна быть выполнена при первом открытии устройства, и эта работа должна быть отменена при последнем закрытии устройства. Код disconnect должен решать проблемы параллелизма в отношении методов open() и close(), а также принудительно выполнять все ожидающие запросы I/O (при необходимости отменяя связь с ними и блокируя их до тех пор, пока отмена связи не завершится).

struct usb_device_driver

Идентифицирует драйвер устройства USB для usbcore.

Определение:

struct usb_device_driver {
const char *name;
bool (*match) (struct usb_device *udev);
int (*probe) (struct usb_device *udev);
void (*disconnect) (struct usb_device *udev);
int (*suspend) (struct usb_device *udev, pm_message_t message);
int (*resume) (struct usb_device *udev, pm_message_t message);
int (*choose_configuration) (struct usb_device *udev);
const struct attribute_group **dev_groups;
struct device_driver driver;
const struct usb_device_id *id_table;
unsigned int supports_autosuspend:1;
unsigned int generic_subclass:1; };

Описание полей:

name Имя драйвера должно быть уникальным среди драйверов USB, и нормально должно совпадать с именем модуля.

match Если установлено, то используется для лучшего соответствия device/driver.

probe Вызывается для проверки готовности драйвера к управлению определенным устройством. Если это так, probe возвращает ноль и использует dev_set_drvdata(), чтобы связать данные драйвера с устройством. Если вы не хотите управлять устройством, то верните отрицательное значение errno.

disconnect Вызывается, когда устройство больше недоступно, обычно из-за того, что оно отключено (или отключается), или модуль драйвера выгружается.

suspend Вызывается, когда устройство переводится системой в состояние приостановки.

resume Вызывается, когда устройство возобновляется (выводится из приостановки) системой.

choose_configuration Если не NULL, то вызывается вместо умолчания usb_choose_configuration(). Если это возвращает ошибку, то мы перейдем к вызову обычного usb_choose_configuration().

dev_groups Атрибуты, подсоединенные к устройству, которые будут созданы после привязки к драйверу.

driver Базовая структура модели драйвера.

id_table Используется вместе с match() для выбора лучшего подходящего драйвера во время probe().

supports_autosuspend Если установлено в 0, то USB core не позволит выполнять автоматическую приостановку (autosuspend) для устройств, привязанных к этому драйверу.

generic_subclass Если установлено в 1, то generic-функции probe, disconnect, resume и suspend драйвера USB будут вызваны в дополнение к собственным функциям драйвера, поэтому эту часть настройки не нужно реплицировать.

Описание:

Драйверы USB должны предоставлять все поля, перечисленные выше, кроме driver, match и id_table.

struct usb_class_driver

Идентифицирует драйвер USB, который хочет использовать основной (major) номер USB.

Определение:

struct usb_class_driver {
char *name;
char *(*devnode)(const struct device *dev, umode_t *mode);
const struct file_operations *fops;
int minor_base; };

Описание полей:

name Имя USB-класса устройства для этого драйвер. Будет показано в sysfs.

devnode Callback-фунция для предоставления подсказки именования возможного узла устройства (device node) при создании.

fops Указатель на структуру file_operations этого драйвера.

minor_base Начала minor-диапазона для этого драйвера.

Описание:

Эта структура используется для функций usb_register_dev() и usb_deregister_dev(), чтобы объединить ряд используемых для них параметров.

module_usb_driver (__usb_driver)

Вспомогательный макрос для регистрации драйвера USB.

Параметры:

__usb_driver Структура usb_driver.

Описание:

Вспомогательный макрос для драйверов USB, который не делает ничего специального в модуле init/exit. Это несколько уменьшает перегруженность кода. Каждый модуль может использовать этот макрос только однократно, и его вызов заменяет module_init() и module_exit().

struct urb

USB Request Block

Определение:

struct urb {
struct list_head urb_list;
struct list_head anchor_list;
struct usb_anchor *anchor;
struct usb_device *dev;
struct usb_host_endpoint *ep;
unsigned int pipe;
unsigned int stream_id;
int status;
unsigned int transfer_flags;
void *transfer_buffer;
dma_addr_t transfer_dma;
struct scatterlist *sg;
struct sg_table *sgt;
int num_mapped_sgs;
int num_sgs;
u32 transfer_buffer_length;
u32 actual_length;
unsigned char *setup_packet;
dma_addr_t setup_dma;
int start_frame;
int number_of_packets;
int interval;
int error_count;
void *context;
usb_complete_t complete;
struct usb_iso_packet_descriptor iso_frame_desc[]; };

Описание полей:

urb_list Для использования текущим владельцем этого URB.

anchor_list Членство в списке anchor.

anchor Для постановки URB-ов на якорь к общему причалу.

dev Идентифицирует устройство USB для выполнения запроса.

ep Указывает на структуру данных конечной точкиPoints to the endpoint’s data structure. В конечном итоге заменит pipe.

pipe Хранит номер конечной точки, направление (direction), тип, и другое. Создайте эти значения с помощью 8 доступных макросов; usb_{snd,rcv}TYPEpipe(dev,endpoint), где TYPE это "ctrl" (control), "bulk", "int" (interrupt) или "iso" (isochronous). Например usb_sndbulkpipe() или usb_rcvintpipe(). Диапазон номеров конечных точек от 0 до 15. Обратите внимание, что "in" конечная точка 2 отличается от конечной точки (и pipe) "out" конечной точки 2. Текущая конфигурация управляет существованием, типом и максимальным размером пакета любой данной конечной точки.

stream_id Идентификатор потока конечной точки (stream ID) для bulk-потоков.

status Это считывается в non-iso функциях завершения для получения состояния конкретного запроса. ISO-запросы используют его только для того, чтобы сообщить, был ли URB отсоединен (unlinked); подробный статус для каждого кадра находится в полях iso_frame_desc.

transfer_flags Различные флаги могут использоваться, чтобы влиять на то, как обрабатываются передача URB (submission), разрыв связи (unlinking) или его операция. Различные виды URB-ов могут использовать разные флаги.

transfer_buffer Идентифицирует буфер, в который (или из которого) будет выполняться I/O запрос, если не установлен URB_NO_TRANSFER_DMA_MAP (однако даже в этом случае не оставляйте мусор в transfer_buffer). Этот буфер должен быть подходящим для DMA; выделяйте его через kmalloc() или эквивалентную функцию. Для транзакций в конечные точки "in" содержимое этого буфера будет изменено. Этот буфер используется на стадии данных транзакций управления (control transfers).

transfer_dma Когда transfer_flags включают URB_NO_TRANSFER_DMA_MAP, драйвер устройства говорит, что он предоставил этот адрес DMA, который драйвер контроллера хоста должен использовать вместо transfer_buffer.

sg Список буферов сбора и разброса (scatter gather), размер буфера каждого элемента в списке (кроме последнего) должен нацело делиться на максимальный размер пакета конечной точки, если no_sg_constraint не установлен в 'struct usb_bus'.

sgt Используется для хранения scatter gather таблицы, возвращенной usb_alloc_noncoherent(), которая описывает выделенную некогерентную и возможно несмеждую память, и гарантированно имеет 1 единственный сегмент отображения DMA. Выделенная память должна быть освобождена через usb_free_noncoherent().

num_mapped_sgs Количество (внутренне) отображенных элементов sg.

num_sgs Количество элементов в списке sg.

transfer_buffer_length Размер transfer_buffer. Транзакция может быть разбита на порции в соответствии с текущим максимальным размером пакета для конечной точки, который является функцией конфигурации и закодирован в pipe. Когда длина 0, не используются ни transfer_buffer, ни transfer_dma.

actual_length Это считывается в non-iso функциях завершения, и говорит о том, сколько байт (из transfer_buffer_length) было передано. Обычно это такое же значение, как запрошенное, кроме ситуации сообщения об ошибке или когда выполнено короткое чтение. Флаг транзакции URB_SHORT_NOT_OK может использоваться, чтобы о таких коротких чтениях сообщалось как об ошибках.

setup_packet Используется только для транзакций управления (control transfers), указывает на 8 байт данных setup. Транзакции управления всегда начинаются отправкой этих данных в устройство. Затем transfer_buffer считывается или записывается по мере необходимости.

setup_dma DMA-указатель на пакет setup. Вызывающий код не должен использовать это поле; setup_packet должен указывать на допустимый буфер.

start_frame Возвратит начальный кадр для изохронных транзакций.

number_of_packets Количество буферов передачи ISO.

interval Указывает интервал опроса для транзакций interrupt или isochronous. Единицы это кадры (миллисекунды) для устройств full и low скоростей, и микрокадры (1/8 миллисекунды) для устройств high и SuperSpeed скоростей.

error_count Возвратит количество транзакций ISO, которые сообщают об ошибках.

context Для использования в функциях завершения. Это нормально указывает на контекст, специфичный для запроса драйвера.

complete Обработчик завершения. Этот URB передан в качестве параметра для функции завершения. Функция завершения может тогда делать с URB все, что захочет, включая его повторную отправку или освобождение.

iso_frame_desc Используется для предоставления массивов буферов транзакций ISO и для сбора статуса транзакции для каждого буфера.

Описание:

Эта структура идентифицирует запросы транзакций USB. URB-ы должны быть выделены вызовом usb_alloc_urb() и освобождаться вызовом usb_free_urb(). Инициализация может быть выполнена различными функциями usb_fill_*_urb(). URB-ы предоставляются с помощью usb_submit_urb(), и ожидающие выполнения запросы могут быть отменены с помощью usb_unlink_urb() или usb_kill_urb().

Буферы транзакций данных (Data Transfer Buffer):

Нормально драйверы предоставляют буферы I/O, выделенные через kmalloc(), или иным способом взятые из общего пула страниц (general page pool). Это обеспечивается transfer_buffer (запросы управления также используют setup_packet), и драйверы контроллера хоста выполняют отображение DMA (и отмену отображения) для каждого переданного буфера. Эти операции отображения могут быть дорогими на нехоторых платформах (возможно с использованием буферов отказов DMA или общения с IOMMU), хотя они дешевы на обычном оборудовании x86 и ppc.

Альтернативно драйверы могут передать флаг транзакции URB_NO_TRANSFER_DMA_MAP, который говорит драйверу контроллера хоста, что такое отображение не нужно для transfer_buffer, поскольку драйвер устройства осведомлен о DMA. Например, драйвер устройства может выделить буфер DMA с помощью usb_alloc_coherent() или usb_buffer_map(). Когда предоставлен этот флаг транзакции, драйверы контроллера хоста будут пытаться использовать адрес DMA, найденный в поле transfer_dma вместо самостоятельного определения адреса DMA.

Имейте в виду, что transfer_buffer все еще должнен быть установлен, если контроллер не поддерживает DMA (как показано hcd_uses_dma()), и при общении с root hub. Если вам приходися переходить между зной highmem и устройством на таком контроллере, создайте буфер отказов (bounce buffer) или выйдите с ошибкой. Если transfer_buffer не может быть установлен (находится в highmem), и контроллер поддерживает DMA, назначьте ему NULL, чтобы usbmon знал, что не надо использовать это значение. setup_packet должен быть всегда установлен, так что он не может быть выделен в highmem.

Инициализация:

Все отправленные URB должны инициализировать поля dev, pipe, transfer_flags (может быть 0) и complete. Все URB должны также инициализировать transfer_buffer и transfer_buffer_length. Они могут предоставить флаг транзакции URB_SHORT_NOT_OK, который показывает, что короткие чтения обрабатываются как ошибки; этот флаг недопустим для запросов записи.

Bulk URB-ы могут установить флаг транзакции URB_ZERO_PACKET, который показывает, что транзакции bulk OUT должны всегда завершаться short-пакетом, даже если это подразумевает добавление дополнительного пакета нулевой длины (zero length packet).

Control URB-ы должны предоставить допустимый указатель в поле setup_packet. В отличие от transfer_buffer, setup_packet может быть не сопоставлен заранее с DMA.

Interrupt URB-ы должны предоставить interval, говорящиц о том, как часто (в единицах миллисекунд или, для highspeed устройств, в единицах 125 микросекунд) опрашивать на предмет транзакций. После того, как URB был предоставлен, поле interval отражает то, как была фактически запланирована транзакция. Интервал опроса может быть более частым, чем запрошен. Например, некоторые контроллеры имеют максимальный интервал 32 миллисекунды, в то время как другие поддерживают интервалы до 1024 миллисекунд. Изохронные URB-ы также имеют интервалы транзакций (обратите внимание, что для изохронных конечных точек, как и для высокоскоростных interrupt конечных точек, кодирование интервала транзакции в дескрипторе конечной точки логарифмическое. Драйверы устройств должны сами преобразовать это значение в линейные единицы).

Если очередь изохронных конечных точек еще не запущена, то контроллер хоста будет планировать запуск новых URB, как только позволит использование полосы пропускания. Если очередь запущена, то новые URB будут планироваться для запуска в первом слоте транзакции после окончания предыдущего URB, если этот слот еще не истек. Если слот истек (что может произойти, когда доставка IRQ отложена на долгое время), поведение планирования зависит от флага URB_ISO_ASAP. Если этот флаг очищен, то URB будет запланирован для запуска в истекшем слоте, подразумевая, что некоторые его пакеты не будут переданы; если этот флаг установлен, то URB будет запланирован для первого не истекшего слота, нарушая синхронизацию очереди. При завершении URB поле start_frame будет установлено в номер (micro)кадра, в котором была запланирована транзакция. Диапазоны счетчика кадров специфичных для контроллера хоста, и могут меняться от 256 до 65536 кадров.

Isochronous URB-ы имеют другую модель транзакции данных, отчасти потому, что качество обслуживания у них только лишь "best effort". Вызывающий код предоставляет специально выделенные URB, с number_of_packets структур iso_frame_desc structures в конце. Каждый такой пакет это индивидуальная ISO транзакция. Изохронные URB обычно ставяться в очередь, предоставляются драйверами, чтобы обеспечить двойную по меньшей мере буферизацию транзакций, и затем повторно предоставлены в обработчиках завершения, так что потоки данных (таких как звук или видео) передаются с постоянной скоростью, какую может поддерживать планировщик контроллера хоста.

Функции обратного вызва завершения (Completion Callbacks):

Callback-функция завершения выполняется в in_interrupt(), и одним из первых действий, которые должнен выполнить обработчик завершения, является проверка поля status. Поле status предоставляется для всех URB-ов. Это используется для сообщения об отсоединенных (unlinked) URB, и состояния всех non-ISO транзакций. Статус не должен проверяться до возвращения URB обработчику завершения.

Поле context нормально исопльзуется для привязки URB-ов обратно к соответствующему драйверу или состоянию запроса.

Когда callback-функция завершения привлекается для неизохронных URB, поле actual_length говорит от том, сколько байт было передано. Это поле обновляется даже когда URB прерван с ошибкой или был отсоединен (unlinked).

Статус транзакции ISO указывается в полях status и actual_length массива iso_frame_desc, а количество ошибок сообщается значением error_count. Callback-функции для транакций ISO будут нормально (повторно)предоставлять URB-ы для гарантирования постоянной скорости передачи.

Обратите внимание, что поля даже с пометкой "public" не должны затрагиваться драйвером, когда URB принадлежит HCD, т. е. с момента вызова usb_submit_urb() до входа в подпрограмму завершения. 

void usb_fill_control_urb (struct urb *urb,
                           struct usb_device *dev,
                           unsigned int pipe,
                           unsigned char *setup_packet,
                           void *transfer_buffer,
                           int buffer_length,
                           usb_complete_t complete_fn,
                           void *context)

Инициализирует URB управления.

Параметры:

struct urb *urb Указатель на URB для инициализации.

struct usb_device *dev Указатель на структуру usb_device для этого URB.

unsigned int pipe Канал конечной точки (endpoint pipe).

unsigned char *setup_packet Указатель на буфер setup_packet. Этот буфер должен быть подходящим для DMA.

void *transfer_buffer Указатель на буфер транзакции. Этот буфер должен быть подходящим для DMA.

int buffer_length Длина буфера транзакции.

usb_complete_t complete_fn Указатель на функцию usb_complete_t.

void *context Что установить в контексте URB.

Описание:

Инициализирует urb управления правильной информацией для предоставления устройству.

Буфер транзакции transfer_buffer и буфер setup_packet будут вероятно заполненыи ли прочитаны через DMA. Самый простой способ получить буфер, подходящий для DMA - выделить его вызовом kmalloc() или эквивалентной функцией, даже ддя очень маленьких буферов. Если эти буферы встроены в более крупную структуру, то есть риск, что сами эти буферы и/или предыдущие/последующие поля повредятся из-за некогерентности кэша; или произойдет замедление, если они будут вытеснены из кэша. Для дополнительной информации см. описание структуры urb.

void usb_fill_bulk_urb (struct urb *urb,
                        struct usb_device *dev,
                        unsigned int pipe,
                        void *transfer_buffer,
                        int buffer_length,
                        usb_complete_t complete_fn,
                        void *context)

Макрос, помогающий использовать bulk URB.

Параметры:

struct urb *urb Указатель на URB для инициализации.

struct usb_device *dev Указатель на структуру usb_device для этого URB.

unsigned int pipe Канал конечной точки (endpoint pipe).

void *transfer_buffer Указатель на буфер транзакции. Этот буфер должен быть подходящим для DMA.

int buffer_length Длина буфера транзакции.

usb_complete_t complete_fn Указатель на функцию usb_complete_t.

void *context Что установить в контексте URB.

Описание:

Инициализирует bulk URB подходящей информацией, необходимой для предоставления устройству. См. usb_fill_control_urb() для описания требований к transfer_buffer.

void usb_fill_int_urb (struct urb *urb,
                       struct usb_device *dev,
                       unsigned int pipe,
                       void *transfer_buffer,
                       int buffer_length,
                       usb_complete_t complete_fn,
                       void *context,
                       int interval)

Макрос, помогающий использовать interrupt URB.

Параметры:

struct urb *urb Указатель на URB для инициализации.

struct usb_device *dev Указатель на структуру usb_device для этого URB.

unsigned int pipe Канал конечной точки (endpoint pipe).

void *transfer_buffer Указатель на буфер транзакции. Этот буфер должен быть подходящим для DMA.

int buffer_length Длина буфера транзакции.

usb_complete_t complete_fn Указатель на функцию usb_complete_t.

void *context Что установить в контексте URB.

int interval Что установить для интервала URB, закодированного как значение bInterval дескриптора конечной точки.

Описание:

Инициализирует interrupt URB подходящей информацией, необходимой для предоставления устройству. См. usb_fill_control_urb() для описания требований к transfer_buffer.

Обратите внимание, что High Speed и SuperSpeed(+) конечные точки, используют логарифмическое кодирование интервала конечной точки, и выражают интервалы опроса в микрофреймах (1/8 миллисекунды) вместо выражения в кадрах (1 на миллисекунду).

int usb_urb_dir_in (struct urb *urb)

Проверка, описывает ли URB транзакцию IN.

Параметры:

struct urb *urb Проверяемый URB.

Возвратит 1, если URB описывает транзакцию IN (device-to-host, от устройства к хосту), иначе возвратит 0.

int usb_urb_dir_out (struct urb *urb)

Проверка, описывает ли URB транзакцию OUT.

Параметры:

struct urb *urb Проверяемый URB.

Возвратит 1, если URB описывает транзакцию OUT (host-to-device, от хоста к устройству), иначе возвратит 0.

struct usb_sg_request

Поддержка scatter/gather I/O.

Определение:

struct usb_sg_request {
int status;
size_t bytes; };

Описание полей:

status 0 показывает успех, иначе отрицательное значение errno.

bytes Количество переданных байт.

Описание:

Эти запросы инициализируются с помощью usb_sg_init(), и затем используются в качестве дескрипторов запросов, переданных в usb_sg_wait() или usb_sg_cancel(). Большинство членов объекта запроса не предназначены для доступа драйверов.

Значения status и bytecount достоверны только после возврата usb_sg_wait(). Если status 0, то bytecount соответствует общему количеству из запроса.

После ошибочного завершения драйверу может понадобиться очистить состояние останова (halt condition) на конечной точке.

[USB Core API]

Существует 2 базовые модели I/O в USB API. Самая простая из них асинхронная: драйверы предоставляют запросы в виде URB, и функции обратного вызова завершения (URB completion callback) обрабатывают следующий шаг. Все типы транзакций USB поддерживают эту модель, хотя есть специальные случаи для control URB-ов (у которых всегда есть стадии setup и status, но у них может не быть стадии data) и isochronous URB-ов (которые позволяют большие пакеты и включают в каждый пакет сообщения отказа, fault reports). Поверх асинхронного реализовано синхронное API, где драйвер вызывает подпрограмму, которая выделяет один или несколько URB, отправляет их и ждет их завершения. Существуют synchronous обертки для однобуферных control транзакций и bulk транзакций (которые неудобно использовать в некоторых сценариях отключения драйвера), а также для потокового ввода/вывода, основанного на scatterlist (bulk или interrupt).

Драйверы USB нуждаются в предоставлении буферов, которые могут использоваться совместно с DMA, хотя они сами необязательно должны предоставлять отображение DMA. Существует API для выделения буферов DMA, которое может предотвратить использование буферов отказа (bounce buffers) на некоторых системах. В некоторых случаях драйверы могут полагаться на 64bit DMA для устранения другого вида bounce buffer.

void usb_init_urb (struct urb *urb)

Инициализирует URB, который может использоваться драйвером USB.

Параметры:

struct urb *urb Указатель на URB для инициализации.

Описание:

Инициализирует URB, который может правильным образом использовать подсистема USB.

Если URB создан с вызовом usb_alloc_urb(), то эту функцию вызвывать не надо. Используйте usb_init_urb только если вы выделяете место для структуры URB, которую используете для себя. Есом вы вызвали эту функцию, то будьте внимательны, когда освобождаете память для вашего URB - он не должен находиться в использовании USB core.

Используйте эту функцию только если вы НА САМОМ ДЕЛЕ понимаете, что делаете.

struct urb *usb_alloc_urb (int iso_packets, gfp_t mem_flags)

Создает новый URB для использования драйвером USB.

Параметры:

int iso_packets Количество ISO-пакетов для этого URB.

gfp_t mem_flags Тип памяти для выделения, см. kmalloc() для списка допустимых для этого опций.

Описание:

Создаст URB для использования драйвером USB, инициализирует несколько внутренних структур, инкрементирует счетчик использования (usage counter) и возвратит указатель на структуру URB.

Если драйвер хочет использовать этот URB для конечных точек interrupt, control или bulk, то передайте 0 в качестве количества ISO-пакетов (iso_packets).

Драйвер должен вызвать usb_free_urb(), когда завершит работу с этим URB.

Возвращаемое значение: указатель на новый созданный URB, или NULL если недостаточно памяти.

void usb_free_urb (struct urb *urb)

Освободит память, используемую URB, когда все пользователи завершили работу с ним.

Параметры:

struct urb *urb Указатель на URB для освобождения, может быть NULL.

Описание:

Должен быть вызван, когда пользователь URB завершил с ним работу. Когда последний пользователь этого URB вызовет эту функцию, память URB освобождается.

Замечание: буфер транзакции, связанный с этим URB, не освобождается, если не установлен флаг URB_FREE_BUFFER.

struct urb *usb_get_urb (struct urb *urb)

Инкрементирует счетчик ссылок на URB.

Параметры:

struct urb *urb Указатель на URB для модификации, может быть NULL.

Описание:

Эта функция должна быть вызвана всякий раз, когда URB передается из драйвера устройства в драйвер контроллера хоста. Это позволяет реализовать правильный учет ссылок для URB-ов.

Возвращаемое значение: указатель на URB с увеличенным счетчиком ссылок.

void usb_anchor_urb (struct urb *urb, struct usb_anchor *anchor)

Связывает URB с якорем перед отправкой вызовом usb_submit_urb.

Параметры:

struct urb *urb Указатель на URB для привязки к якорю.

struct usb_anchor *anchor Указатель на якорь.

Описание:

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

void usb_unanchor_urb (struct urb *urb)

Отмена якоря URB.

Параметры:

struct urb *urb Указатель на URB с якорем.

Описание:

Вызовите эту функцию, чтобы система перестала отслеживать этот URB.

Функции usb_anchor_urb и usb_unanchor_urb предназначены для упрощения управления группами асинхронных запросов (URB) в драйверах USB для Linux, что особенно важно для корректной обработки отключений устройств и предотвращения утечек ресурсов.

[Предназначение и принцип работы]

Эти функции работают со структурой usb_anchor, которая представляет собой набор URB-ов. Вот как выглядит эта структура:

struct usb_anchor {
struct list_head urb_list; // Связанный список URB-ов
wait_queue_head_t wait; // Очередь ожидания завершения URB-ов
spinlock_t lock; // Spinlock для безопасной работы с списком
atomic_t suspend_wakeups; // Счётчик URBs, препятствующих suspend
unsigned int poisoned:1; // Флаг ошибки, блокирующий новые операции };

- usb_anchor_urb связывает URB с якорем перед его отправкой через usb_submit_urb. URB добавляется в список urb_list, и ядро само начинает отслеживать его состояние.
- usb_unanchor_urb удаляет URB из списка якоря, что обычно происходит автоматически в обработчике завершения URB после его выполнения.

[Типичная последовательность использования]

// 1. Инициализация якоря
struct usb_anchor submitted; init_usb_anchor(&submitted);

// 2. Создание и настройка URB
struct urb *urb = usb_alloc_urb(0, GFP_KERNEL);
// ... настройка URB (буфер, конечная точка и т.д.)

// 3. Привязка и отправка usb_anchor_urb(urb, &submitted); // Связать URB с якорем usb_submit_urb(urb, GFP_KERNEL); // Отправить запрос

// 4. В обработчике завершения
void urb_complete(struct urb *urb) {
usb_unanchor_urb(urb); // Автоматически удаляется из списка
// ... обработка данных }

// 5. Управление группой URB-ов (например, при отключении устройства) usb_kill_anchored_urbs(&submitted); // Отменяет все URB в якоре

[Преимущества использования якорей]

Использование якорей решает несколько ключевых проблем управления URB:

- Безопасность: встроенная spinlock-блокировка защищает список URBs от конкурентного доступа, предотвращая состояния гонки.
- Упрощение кода: функции usb_kill_anchored_urbs и usb_wait_anchor_empty_timeout позволяют одной операцией отменить или дождаться завершения всех URB в якоре.
- Интеграция с управлением питанием: якорь автоматически отслеживает URB-ы, которые могут препятствовать переходу устройства в спящий режим, через счетчик suspend_wakeups.
- Обработка ошибок: флаг poisoned позволяет пометить якорь как нерабочий, чтобы предотвратить отправку новых запросов при критических ошибках.

int usb_pipe_type_check (struct usb_device *dev, unsigned int pipe)

Проверка работоспособности (sanity check) конкретного pipe для USB-устройства.

Параметры:

struct usb_device *dev Структура usb_device для проверки.

unsigned int pipe Проверяемый pipe.

Описание:

Выполнит облегченный sanity check для конечной точки указанного устройства USB. Возвратит 0, если pipe достоверен для определенного устройства USB, иначе возвратит отрицательный код ошибки.

int usb_urb_ep_type_check (const struct urb *urb)

Проверка работоспособности (sanity check) конечной точки в конкретном URB.

Параметры:

const struct urb *urb Проверяемый URB.

Описание:

Выполнит облегченный sanity check для конечной точки в указанном URB. Возвратит 0, если URB содержит достоверную конечную точку, иначе возвратит отрицательный код ошибки.

int usb_submit_urb (struct urb *urb, gfp_t mem_flags)

Выдаст асинхронный запрос транзакции для конечной точки.

Параметры:

struct urb *urb Указатель на URB, описывающий запрос.

gfp_t mem_flags Тип памяти для выделения, см. описание kmalloc() для списка допустимых для этого опций.

Описание:

Выдаст запрос транзакции и передает управление URB, описывающим этот запрос, в подсистему USB. Завершение запроса будет асинхронно показано позже, путем вызова обработчика завершения (completion handler). Три типа завершения это success, error и unlink (программно-индуцированный отказ, так называемый "request cancellation").

URB-ы могут быть выданы в контексте прерывания.

Вызывающий код должен корректно инициализировать URB перед выдачей запроса. Такие функции, как usb_fill_bulk_urb() и usb_fill_control_urb() доступны для гарантии, что большинство полей URB корректно инициализированы, в частности вид транзакции, хотя они не инициализируют никакие флаги транзакции.

Если отправка запроса была успешной, то callback-функция complete() из URB будет вызвана ровно 1 раз, когда USB core и Host Controller Driver (HCD) завершат работу с этим URB. Когда вызывается функция завершения, управление URB возвращается драйверу устройства, который выдал запрос. Обработчик завершения может тогда немедленно освободить этот URB или использовать его повторно.

За некоторыми исключениями, драйвер устройства USB никогда не должен обращаться к полям URB, предоставленного usbcore или HCD, пока не будет вызван complete(). Исключения связаны с планированием периодических транзакций (periodic transfer scheduling). И для interrupt URB, или для isochronous URB, как часть успешного запроса URB поле urb->interval модифицируется, чтобы отражать фактический используемый период транзакции (нормально некоторая степень двойки). И для изохронных URB поле urb->start_frame модифицируется, чтобы отражать информацию, когда транзакции URB-ов запланированы для запуска.

Не все изохронные политики планирования передачи будут работать, но большинство драйверов хост-контроллеров должны легко обрабатывать очереди ISO, начиная с настоящего момента до 10-200 мс в будущем. Драйверы должны пытаться удерживать в очереди не менее 1 .. 2 мс данных; многие контроллеры требуют, чтобы новые транзакции начинались не менее чем через 1 мс после их добавления. Если драйвер не справляется и очередь опустеет, поведение для новых отправок регулируется флагом URB_ISO_ASAP. Если флаг установлен или очередь простаивает, то URB всегда назначается первому доступному (и еще не истекшему) слоту в расписании конечной точки. Если флаг не установлен и очередь активна, то URB всегда назначается следующему слоту в расписании, следующему за концом предыдущего URB конечной точки, даже если этот слот находится в прошлом. Если пакет назначается таким образом слоту, срок действия которого уже истёк, он не передаётся, и соответствующее поле статуса usb_iso_packet_descriptor вернёт значение -EXDEV. Если это произойдёт со всеми пакетами в URB, отправка завершится ошибкой -EXDEV.

Для конечных точек управления (control endpoints), вместо этого вызова часто используется синхронный вызов usb_control_msg() (для контекста не прерывания). Это часто используется через удобные обёртки для запросов, стандартизированных в спецификации USB 2.0. Для bulk конечных точек доступен синхронный вызов usb_bulk_msg().

Постановка запросов в очередь (Request Queuing):

URB-запросы могут быть отправлены конечным точкам до завершения предыдущих запросов, чтобы минимизировать влияние задержек прерываний и системных издержек на пропускную способность данных. При такой политике очередей очередь конечной точки никогда не будет пустой. Это необходимо для непрерывных изохронных потоков данных, а также может потребоваться для некоторых видов передачи прерываний. Такая организация очереди требуется для для изохронных потоков данных, что также максимизирует использование полосы пропускания, позволяя USB-контроллерам начинать обработку последующих запросов до того, как программное обеспечение драйвера завершит обработку более ранних (успешных) запросов.

Начиная с Linux 2.6, все очереди транзакций конечной точки USB поддерживают глубину больше единицы. Ранее это было характерно для поведения HCD, кроме транзакций ISO. Неизохронные очереди конечной точки неактивны во время очистки после сбоев (transfer errors или cancellation).

Транзакции с зарезервированной полосой пропускания (Reserved Bandwidth Transfers):

Периодические передачи (interrupt или isochronous) выполняются многократно с интервалом, указанным в URB. Отправка первого URB в конечную точку резервирует полосу, необходимую для этих транзакций. Если подсистема USB не может выделить достаточную полосу для выполнения периодического запроса, то отправка такого периодического запроса должна завершиться ошибкой.

Для устройств под управлением xHCI пропускная способность резервируется во время настройки или при выборе альтернативной настройки (alt setting). Если пропускной способности шины недостаточно, запрос конфигурации/альтернативного параметра завершится ошибкой. Таким образом, отправка данных на периодические конечные точки на устройствах под управлением xHCI никогда не должна прерываться из-за ограничений пропускной способности.

Драйверы устройств должны явно запрашивать такое повторение, обеспечивая постоянное наличие некоторого URB в очереди конечной точки (за исключением, возможно, коротких периодов во время calback-вызовов завершения). Когда в очереди больше нет URB, резервирование пропускной способности конечной точки отменяется. Это означает, что драйверы могут использовать свои обработчики завершения, чтобы гарантировать сохранение необходимой пропускной способности, повторно инициализируя и отправляя только что завершённый URB до тех пор, пока драйверу не перестанет требоваться эта периодическая пропускная способность.

Флаги памяти:

Общие правила выбора mem_flags те же, что и для kmalloc. Возможны четыре значения: GFP_KERNEL, GFP_NOFS, GFP_NOIO и GFP_ATOMIC.

GFP_NOFS никогда не используется, поскольку еще не реализован.

GFP_ATOMIC используется:

a) когда вы находитесь внутри обработчика завершения, внутри прерывания, нижней половины, тасклета или таймера, или
b) удерживаете спин-блокировку или блокировку чтения/записи rwlock (не относится к семафорам), или
c) current->state != TASK_RUNNING, это имеет место только после того, как вы его изменили.

GFP_NOIO используется в блокировки пути IO и обработке ошибок устройств хранения.

Все другие ситуации используют GFP_KERNEL.

Можно вывести еще несколько конкретных правил для mem_flags, таких как:

1. методы start_xmit, timeout и receive сетевых драйверов должны использовать GFP_ATOMIC (они вызываются с удерживаемой спин-блокировкой);
2. методы queuecommand драйверов scsi должны использовать GFP_ATOMIC (также вызываются с удерживаемой спин-блокировкой);
3. если вы используете поток ядра с сетевым драйвером, вы должны использовать GFP_NOIO, если не применяются условия (b) или (c);
4. после выполнения down() вы можете использовать GFP_KERNEL, если не применяются условия (b) или (c) или вы не находитесь в пути ввода-вывода блока драйвера хранилища;
5. USB probe и disconnect могут использовать GFP_KERNEL, если не применяются пункты (b) или (c);
6. изменение прошивки на работающем хранилище или сетевом устройстве использует GFP_NOIO, если не применяются пункты b) или c).

Возвращаемое значение: 0 при успешной отправке, иначе отрицательное значение ошибки.

int usb_unlink_urb (struct urb *urb)

Отменит (abort/cancel) запрос транзакции для конечной точки.

Параметры:

struct urb *urb Указатель на URB, описывающий ранее выданный запрос, может быть NULL.

Описание:

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

Драйверы не должны вызывать эту подпрограмму или связанные подпрограммы, такие как usb_kill_urb(), после того, как был выполнен возврат из их метода disconnect. Функция disconnect должна синхронизироваться с процедурами ввода/вывода драйвера, чтобы убедиться, что все действия, связанные с URB, завершены до её возврата.

Этот запрос асинхронный, однако HCD может вызвать callback-функцию ->complete() во время unlink. Таким образом, когда драйверы вызывают usb_unlink_urb(), они могут удерживать любые блокировки, которые могут быть взяты функцией завершения. Успех показывается возвратом -EINPROGRESS, когда URB вероятно еще не будет возвращен драйверу устройства. При вызове функция завершения увидит urb->status == -ECONNRESET. Ошибка указывается функцией usb_unlink_urb(), возвращающей любое другое значение. Отсоединение завершится неудачей, если urb в данный момент не "присоединен" (not linked, т.е. он никогда не был отправлен, был отсоединен ранее или оборудование уже завершило работу с ним), даже если обработчик завершения ещё не запущен.

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

Unlinking и очереди конечной точки (Endpoint Queues):

Примечание: поведения и гарантии, описанные ниже, не применяются к виртуальным корневым хабам (virtual root hubs), но только к очередям конечной точки для физических устройств USB.

Драйверы контроллеров хоста (HCD) помещают все URB в очередь для определенной конечной точки. Обычно очередь продвигается по мере обработки каждого запроса аппаратным обеспечением контроллера. Но когда URB завершается с ошибкой, её выполнение обычно останавливается (см. ниже), по крайней мере, до тех пор, пока не будет выполнена процедура завершения этого URB. Гарантируется, что остановленная очередь не перезапустится, пока все её несвязанные URB не будут полностью удалены, и их процедуры завершения не будут выполнены, даже если это произойдёт только через некоторое время после возврата из исходного обработчика завершения. Аналогичное поведение и гарантия применяются, когда URB завершается из-за того, что он был unlinked.

Очереди конечной точки bulk и interrupt гарантированно останавливаются при завершении URB с любой ошибкой, включая -ECONNRESET, -ENOENT и -EREMOTEIO. Очереди конечных точек управления ведут себя аналогично, за исключением того, что их остановка при ошибках -EREMOTEIO не гарантируется. Очереди для изохронных конечных точек обрабатываются иначе, поскольку они должны продвигаться с фиксированной скоростью. Такие очереди не останавливаются при возникновении ошибки или разрыве связи URB. Несвязанный изохронный URB может оставить пробел в потоке пакетов; неизвестно, можно ли заполнить такие пробелы.

Обратите внимание, что досрочное завершение URB из-за получения короткого пакета приведёт к ошибке -EREMOTEIO только в том случае, если установлен флаг URB_SHORT_NOT_OK. Устанавливая этот флаг, драйверы USB-устройств могут создавать длинные очереди для больших или сложных пакетных передач и надёжно очищать их после любой прерванной передачи, отключая все ожидающие URB при первой ошибке.

Когда control URB прерывается с ошибкой, отличающейся от -EREMOTEIO, весьма вероятно, что этап состояния передачи не будет выполнен.

Возвращаемое значение: -EINPROGRESS при успехе. См. описание для других значений при отказе.

void usb_kill_urb (struct urb *urb)

Отменит запрос транзакции и ждет его завершения.

Параметры:

struct urb *urb Указатель на URB, описывающий ранее выданный запрос, может быть NULL.

Описание:

Эта функция отменит выполняющийся запрос. Гарантируется, что после возврата все обработчики завершения будут завершены, а URB будет полностью свободен и доступен для повторного использования. Эти особенности делают этот метод идеальным способом остановки ввода-вывода в функции обратного вызова disconnect() или close(). Если запрос ещё не завершён или не был отсоединён (unlinked), обработчик завершения увидит urb->status == -ENOENT.

Пока эта функция выполняется, попытки повторно выдать URB потерпят неудачу с ошибкой -EPERM. Таким образом, даже если обработчик завершения URB всегда пытается повторно отправить запрос, это не удастся, и URB перейдет в режим ожидания.

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

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

Эта процедура не должна вызываться драйвером после завершения его метода disconnect.

void usb_poison_urb (struct urb *urb)

Надежно прибьет транзакцию и предотвратит будущее использование URB.

Параметры:

struct urb *urb Указатель на URB, описывающий ранее выданный запрос, может быть NULL.

Описание:

Эта функция отменит выполняющийся запрос. Гарантируется, что после возврата все обработчики завершения будут завершены, а URB будет полностью в состоянии ожидания (idle) и не может использоваться повторно. Эти фичи делают этот способ отмены идеальным для остановки ввода-вывода в callback-вызове disconnect(). Если запрос ещё не завершён или не отключён, обработчик завершения увидит urb->status == -ENOENT.

После и во время работы этой подпрограммы попытки повторно выдать URB потерпят неудачу с ошибкой -EPERM. Таким образом, даже если обработчик завершения URB всегда пытается повторно отправить запрос, это не удастся, и URB перейдет в режим ожидания.

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

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

Эта процедура не должна вызываться драйвером после завершения его метода disconnect.

void usb_block_urb (struct urb *urb)

Надежно предотвращает дальнейшее использование URBю

Параметры:

struct urb *urb Указатель на URB, который блокируется, может быть NULL.

Описание:

После запуска этой функции попытки повторно выдать URB завершатся неудачей с ошибкой -EPERM. Таким образом, даже если обработчик завершения URB всегда пытается повторно отправить запрос, это не удастся, и URB перейдет в режим ожидания.

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

void usb_kill_anchored_urbs (struct usb_anchor *anchor)

Прибьет все URB-ы, связанные с якорем.

Параметры:

struct usb_anchor *anchor Якорь, к которому привязаны запросы.

Описание:

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

Эта подпрограмма не должна вызываться драйвером после возврата из его метода disconnect.

void usb_poison_anchored_urbs (struct usb_anchor *anchor)

Прекратит весь трафик из якоря.

Параметры:

struct usb_anchor *anchor Якорь, к которому привязаны запросы.

Описание:

Испортит все находящиеся в ожидании URB, начиная с конца очереди. Новые добавленные URB-ы будут также испорчены.

Эта подпрограмма не должна вызываться драйвером после возврата из его метода disconnect.

void usb_unpoison_anchored_urbs (struct usb_anchor *anchor)

Разрешит повторное успешное использование якоря.

Параметры:

struct usb_anchor *anchor Якорь, к которому привязаны запросы.

Описание:

Обратит эффект usb_poison_anchored_urbs, после возврата из функции usb_unpoison_anchored_urbs якорь может опять нормально использоваться.

void usb_anchor_suspend_wakeups (struct usb_anchor *anchor)

Параметры:

struct usb_anchor *anchor Якорь, на котором вы хотите приостановить пробуждение.

Описание:

Вызовите эту подпрограмму, чтобы предотвратить пробуждение ожидающих usb_wait_anchor_empty_timeout последним откреплённым URB-ом. Это используется в пути возврата URB-ов HCD для отсрочки пробуждения до выхода из обработчика завершения.

void usb_anchor_resume_wakeups (struct usb_anchor *anchor)

Параметры:

struct usb_anchor *anchor Якорь, на котором вы хотите возобновить пробуждение.

Описание:

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

int usb_wait_anchor_empty_timeout (struct usb_anchor *anchor, unsigned int timeout)

Ожидание, когда якорь станет не используемым.

Параметры:

struct usb_anchor *anchor Якорь, который вы хотите сделать неиспользуемым.

unsigned int timeout Как долго вы хотите ожидать в миллисекундах.

Описание:

Вызовите эту функцию, когда хотите убедиться, что все URB-ы якоря завершены.

Возвращаемое значение: не 0, если якорь стал неиспользуемым. 0 при таймауте.

struct urb *usb_get_from_anchor (struct usb_anchor *anchor)

Получение самого старого URB якоря.

Параметры:

struct usb_anchor *anchor Якорь, URB которого вы хотите получить.

Описание:

Позволит взять самый старый urb из якоря, снять его с якоря и возвратить на выходе.

Возвращаемое значение: самый старый URB из якоря, или NULL, если с якорем не связан ни один URB.

void usb_scuttle_anchored_urbs (struct usb_anchor *anchor)

Уничтожит все URB якоря.

Параметры:

struct usb_anchor *anchor Якорь, у которого вы хотите отключить URB-ы.

Описание:

Используйте эту подпрограмму, когда хотите избавиться от всех URB якоря.

int usb_anchor_empty (struct usb_anchor *anchor)

Проверка пустоты якоря.

Параметры:

struct usb_anchor *anchor Якорь, который вы хотите опросить.

Возвращаемое значение: 1, если с якорем не связаны никакие URB-ы.

int usb_control_msg (struct usb_device *dev,
                     unsigned int pipe,
                     __u8 request,
                     __u8 requesttype,
                     __u16 value,
                     __u16 index,
                     void *data,
                     __u16 size,
                     int timeout)

Соберет URB управления, отправит и ждет завершения.

Параметры:

struct usb_device *dev Указатель на устройство USB, которому отправляется сообщение.

unsigned int pipe Канал конечной точки, куда отправляется сообщение.

__u8 request Значение запроса сообщения USB.

__u8 requesttype Значение типа запроса сообщения USB.

__u16 value Значение сообщения USB.

__u16 index Значение индекса сообщения USB.

void *data Указатель на отправляемые данные.

__u16 size Длина в байтах отправляемых данных.

int timeout Время в миллисекундах для ожидания завершения сообщения перед наступлением таймаута (если 0, то ожидание бесконечное).

Контекст: контекст задачи (не прерывание), может быть sleep.

Описание:

Эта функция отправит простое управляющее сообщение на указанную конечную точку и ждет завершения сообщения или наступления таймаута.

Не используйте эту функцию из контекста прерывания. Если вам нужно асинхронное сообщение, или нужно послать сообщение из контекста прерывания, то используйте usb_submit_urb(). Если поток вашего драйвера использует этот вызов, то убедитесь, что ваш метод disconnect() может ждать завершения usb_control_msg. Поскольку у вас нет дескриптора на используемом URB, вы не можете отменить этот запрос.

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

int usb_control_msg_send (struct usb_device *dev,
                          __u8 endpoint,
                          __u8 request,
                          __u8 requesttype,
                          __u16 value,
                          __u16 index,
                          const void *driver_data,
                          __u16 size,
                          int timeout,
                          gfp_t memflags)

Соберет управляющее сообщение "send", пошлет его и ждет завершения.

Параметры:

struct usb_device *dev Указатель на устройство USB, которому отправляется сообщение.

__u8 endpoint Конечная точка, куда посылается сообщение.

__u8 request Значение запроса сообщения USB.

__u8 requesttype Значение типа запроса сообщения USB.

__u16 value Значение запроса USB.

__u16 index Значение индекса сообщения USB.

const void *driver_data Указатель на данные для отправки.

__u16 size Длина в байтах отправляемых данных.

int timeout Время в миллисекундах для ожидания завершения сообщения перед наступлением таймаута (если 0, то ожидание бесконечное).

gfp_t memflags Флаги выделения памяти для буферов.

Контекст: контекст задачи (не прерывание).

Описание:

Эта функция отправляет управляющее сообщение в указанную конечную точку, от которой не ожидается ответа (т. е. "отправления сообщения"), и ждет завершения сообщения или истечения времени ожидания.

Не используйте эту функцию из контекста прерывания. Если вам нужно асинхронное сообщение, или нужно послать сообщение из контекста прерывания, то используйте usb_submit_urb(). Если поток вашего драйвера использует этот вызов, то убедитесь, что ваш метод disconnect() может ждать завершения usb_control_msg_send. Поскольку у вас нет дескриптора на используемом URB, вы не можете отменить этот запрос.

Указатель на данные может создан по ссылке на стек или куда-то еще, так как эта память не будет изменяться. Здесь нет тех ограничений, которые есть у usb_control_msg(), у которой указатель на данные должен быть на динамически выделяемую память (т. е. на память, которую можно успешно передать устройству через DMA).

Возвращаемое значение: при успехе возвратит 0, иначе отрицательный код ошибки.

int usb_control_msg_recv (struct usb_device *dev,
                          __u8 endpoint,
                          __u8 request,
                          __u8 requesttype,
                          __u16 value,
                          __u16 index,
                          void *driver_data,
                          __u16 size,
                          int timeout,
                          gfp_t memflags)

Соберет управляющее сообщение "receive", отправит его и ждет завершения.

Параметры:

struct usb_device *dev Указатель на устройство USB, которому посылается сообщение.

__u8 endpoint Конечная точка, куда посылается сообщение.

__u8 request Значение запроса сообщения USB.

__u8 requesttype Значение типа запроса сообщения USB.

__u16 value Значение запроса USB.

__u16 index Значение индекса сообщения USB.

void *driver_data Указатель на данные, которые должны быть заполнены сообщением.

__u16 size Длина в байтах принимаемых данных.

int timeout Время в миллисекундах для ожидания завершения сообщения перед наступлением таймаута (если 0, то ожидание бесконечное).

gfp_t memflags Флаги выделения памяти для буферов.

Контекст: контекст задачи (не прерывание).

Описание:

Эта функция отправит управляющее сообщение в указанную конечную точку, на которое ожидается заполненный ответ (т. е. "receive message") и ждет завершения сообщения или наступления таймаута.

Не используйте эту функцию из контекста прерывания. Если вам нужно асинхронное сообщение, или нужно послать сообщение из контекста прерывания, то используйте usb_submit_urb(). Если поток вашего драйвера использует этот вызов, то убедитесь, что ваш метод disconnect() может ждать завершения usb_control_msg_recv. Поскольку у вас нет дескриптора на используемом URB, вы не можете отменить этот запрос.

Указатель на данные может создан по ссылке на стек или куда-то еще, так как эта память не будет изменяться. Здесь нет тех ограничений, которые есть у usb_control_msg(), у которой указатель на данные должен быть на динамически выделяемую память (т. е. на память, которую можно успешно передать устройству через DMA).

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

Возвращаемое значение: при успехе возвратит 0, иначе отрицательный код ошибки.

int usb_interrupt_msg (struct usb_device *usb_dev,
                       unsigned int pipe,
                       void *data,
                       int len,
                       int *actual_length,
                       int timeout)

Соберет interrupt URB, отправит его и ждет завершения.

Параметры:

struct usb_device *usb_dev Указатель на устройство USB, в которое отправляется сообщение.

unsigned int pipe Канал конечной точки, в который посылается сообщение.

void *data Указатель на посылаемые данные.

int len Длина в байтах посылаемых данных.

int *actual_length Указатель на ячейку памяти, куда помещается количество фактически переданных байт.

int timeout Время в миллисекундах ожидания завершения сообщения перед наступлением таймаута (0 для бесконечного ожидания).

Контекст: контекст задачи (не прерывание), может быть sleep.

Описание:

Эта функция отправит простое interrupt сообщение для указанной конечной точки и ждет завершения или наступления таймаута.

Не используйте эту функцию из контекста прерывания. Если вам нужно асинхронное сообщение, или нужно послать сообщение из контекста прерывания, то используйте usb_submit_urb(). Если поток вашего драйвера использует этот вызов, то убедитесь, что ваш метод disconnect() может ждать завершения usb_interrupt_msg. Поскольку у вас нет дескриптора на используемом URB, вы не можете отменить этот запрос.

Возвращаемое значение: при успехе вернет 0, иначе отрицательный код ошибки. Количество реально переданных данных сохраняется по указателю параметра actual_length.

int usb_bulk_msg (struct usb_device *usb_dev,
                  unsigned int pipe,
                  void *data,
                  int len,
                  int *actual_length,
                  int timeout)

Соберет bulk URB, отправит его и ждет завершения.

Параметры:

struct usb_device *usb_dev Указатель на устройство USB, в которое отправляется сообщение.

unsigned int pipe Канал конечной точки, в который посылается сообщение.

void *data Указатель на посылаемые данные.

int len Длина в байтах посылаемых данных.

int *actual_length Указатель на ячейку памяти, куда помещается количество фактически переданных байт.

int timeout Время в миллисекундах ожидания завершения сообщения перед наступлением таймаута (0 для бесконечного ожидания).

Контекст: контекст задачи (не прерывание), может быть sleep.

Описание:

Эта функция пошлет простое bulk сообщение для указанной конечной точки и ждет завершения сообщения либо наступление таймаута.

Не используйте эту функцию из контекста прерывания. Если вам нужно асинхронное сообщение, или нужно послать сообщение из контекста прерывания, то используйте usb_submit_urb(). Если поток вашего драйвера использует этот вызов, то убедитесь, что ваш метод disconnect() может ждать завершения usb_bulk_msg. Поскольку у вас нет дескриптора на используемом URB, вы не можете отменить этот запрос.

Поскольку отсутствуют usb_interrupt_msg() и USBDEVFS_INTERRUPT ioctl, пользователи вынуждены злоупотреблять этой процедурой, используя её для отправки URB для конечных точек interrupt. Мы позволим себе создать interrupt URB (с интервалом по умолчанию), если целью является конечная точка interrupt.

Возвращаемое значение: при успехе вернет 0, иначе отрицательный код ошибки. Количество реально переданных данных сохраняется по указателю параметра actual_length.

int usb_sg_init (struct usb_sg_request *io,
                 struct usb_device *dev,
                 unsigned pipe,
                 unsigned period,
                 struct scatterlist *sg,
                 int nents,
                 size_t length,
                 gfp_t mem_flags)

Инициализирует bulk/interrupt запрос ввода-вывода на основе списка разброса (scatterlist).

Параметры:

struct usb_sg_request *io Блок инициализируемого запроса. До тех пор, пока usb_sg_wait() не вернет значение, воспринимайте это как указатель на непрозрачный блок памяти.

struct usb_device *dev Устройство USB, на котором будут отправлены или приняты данные.

unsigned pipe Канал конечной точки для транзакции данных.

unsigned period Частота опроса конечных точек interrupt, в кадрах или (для high speed конечных точек) micro-кадрах; игнорируется для bulk.

struct scatterlist *sg Элементы scatterlist.

int nents Сколько элементов в scatterlist.

size_t length Сколько байт для отправки из scatterlist, или 0 для отправки каждого байта, идентифицированного в списке.

gfp_t mem_flags Флаги SLAB_*, влияющие на выделения памяти для этого вызова.

Описание:

Инициализирует запрос scatter/gather, с выделением ресурсов, таких как отображения ввода/вывода (I/O mappings) и память URB (исключая может быть памяти, используемой драйверами контроллера USB).

Запрос может быть выдан с использованием usb_sg_wait(), который ждет завершения I/O (или отмены) и затем очищаются ресурсы, выделенные usb_sg_init().

Запрос может быть отменен вызовом usb_sg_cancel(), либо перед, либо после вызова usb_sg_wait().

Возвращаемое значение: 0 в случае успеха, иначе отрицательное значение ошибки.

void usb_sg_wait (struct usb_sg_request *io)

Синхронно выполнит запрос scatter/gather.

Параметры:

struct usb_sg_request *io Дескриптор блока запроса, инициализированный вызовом usb_sg_init(). Некоторые поля этой структуры становятся доступны после возврата из usb_sg_wait.

Контекст: контекст задачи, может быть sleep.

Описание:

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

Существует 3 вида завершения для этой функции.

1. Успех, когда io->status ноль. Количество переданных io->bytes байт такое же, как запрошено.
2. Ошибка, когда io->status отрицательное значение errno. Количество переданных байт io->bytes в этом случае обычно меньше, чем запрошено, и может быть ненулевым.
3. Отмена, тип ошибки со статусом -ECONNRESET, когда это инициировано вызовом usb_sg_cancel().

Когда произошел возврат из этой функции, вся выделенная память в usb_sg_init() или в этом вызове будет освобождена. Блок параметра запроса все еще может быть переда в usb_sg_cancel(), или может быть освобожден. Его также можно повторно инициализировать и затем использовать повторно.

Скорости передачи данных (Data Transfer Rates):

Bulk транзакции допустимы для конечных точек скоростей full или high. Самая лучшая full скорость передачи 19 пакетов по 64 байта каждый на кадр, или 1216 байт в миллисекунду. Самая лучшая high скорость передачи 13 пакетов по 512 байта каждый на microframe, или 52 килобайт на миллисекунду.

Причиной использования interrupt транзакций через этот API-вызов, скорее всего заключается в резервировании высокоскоростной полосы пропускания, позволяющей передавать до 24 килобайт в миллисекунду. Эта возможность менее полезна для low speed или full speed транзакций interrupt, которые допускают передачу не более одного пакета в миллисекунду размером не более 8 или 64 байт соответственно.

Нет необходимости вызывать эту функцию для резервирования полосы пропускания для устройств под контроллером хоста xHCI, поскольку полоса резервируется, когда выбрана конфигурация или настройка alt интерфейса.

void usb_sg_cancel (struct usb_sg_request *io)

Остановка scatter/gather I/O, запущенного usb_sg_wait().

Параметры:

struct usb_sg_request *io Блок запроса, инициализированный через usb_sg_init().

Описание:

Остановит запрос после того, как он был запущен вызовом usb_sg_wait(). Вызов usb_sg_cancel также предотвратит запуск запроса, инициализированного usb_sg_init(), но не запущенного, так что вызов usb_sg_cancel просто освободит ресурсы, выделенные для запроса.

int usb_get_descriptor (struct usb_device *dev,
                        unsigned char type,
                        unsigned char index,
                        void *buf, int size)

Выдаст generic-запрос GET_DESCRIPTOR.

Параметры:

struct usb_device *dev Устройство USB, дескриптор которого запрашивается.

unsigned char type Тип дескриптора (USB_DT_*).

unsigned char index Номер дескриптора.

void *buf Куда поместить дескриптор.

int size Размер буфера buf.

Контекст: контекст задачи, может быть sleep.

Описание:

Получит USB-дескриптор. Существуют удобные функции для упрощения получения некоторых типов дескрипторов. Используйте usb_get_string() или usb_string() для USB_DT_STRING. Дескрипторы устройства (USB_DT_DEVICE) и конфигурации (USB_DT_CONFIG) составляют часть структуры устройства. В дополнение к нескольким стандартным USB-дескрипторам некоторые устройства также используют специфичные для класса или вендора дескрипторы.

Этот вызов синхронный, и не может использоваться в контексте прерывания.

Возвращаемое значение: количество полученных байт в случае успеха, или код статуса, возвращенный нижележащим вызовом usb_control_msg().

int usb_string (struct usb_device *dev,
                int index,
                char *buf,
                size_t size)

Возвратит UTF-8 версию строкового дескриптора.

Параметры:

struct usb_device *dev Устройство, дескриптор которого запрашивается.

int index Номер дескриптора.

char *buf Куда поместить строку дескриптора.

size_t size Размер буфера buf.

Контекст: контекст задачи, может быть sleep.

Описание:

Этот вызов преобразует закодированные в UTF-16LE строки, возвращаемые устройствами из usb_get_string_descriptor(), в null-терминированные строки UTF-8, которые более подходят для использования в большинстве контекстов kernel. Обратите внимание, что эта функция выбирает строки в первом языке, поддерживаемом устройством.

Этот вызов синхронный, и не может использоваться в контексте прерывания.

Возвращаемое значение: длина строки (>= 0) или статус usb_control_msg status (< 0).

char *usb_cache_string (struct usb_device *udev, int index)

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

Параметры:

struct usb_device *udev Устройство, строка дескриптора которого считывается.

int index Индекс дескриптора.

Возвращаемое значение: указатель на буфер, выделенный kmalloc, содержащий строку дескриптора, или NULL, если index 0 или строка не может быть прочитана.

int usb_get_status (struct usb_device *dev,
                    int recip,
                    int type,
                    int target,
                    void *data)

Выдаст вызов GET_STATUS.

Параметры:

struct usb_device *dev Устройство, состояние которого проверяется.

int recip USB_RECIP_*; для устройства, интерфейса или конечной точки.

int type USB_STATUS_TYPE_*; для стандартных типов PTM status.

int target 0 (для устройства), иначе номер интерфейса или конечной точки.

void *data Указатель на 2 байта данных bitmap.

Контекст: контекст задачи, может быть sleep.

Описание:

Возвратит статус устройства, интерфейса или конечной точки. Обычно бывает только интересно определить, имеет ли устройство собственное питание (self powered), или разрешен ли на нем функционал удаленного пробуждения (remote wakeup); либо определить, приостановлена ли конечная точка bulk или interrupt (endpoint halted, или "stalled").

Биты в этом bitmap статуса устанавливаются с помощью запроса SET_FEATURE, и очищаются запросом CLEAR_FEATURE. Функция usb_clear_halt() должна использоваться для очистки статуса halt ("stall").

Этот вызов синхронный, и не может использоваться в контексте прерывания.

Возвращаемое значение: при успехе возвратит 0 и значение статуса в ячейке *data (с порядком байт хоста, например на платах LuckFox с чипом ARM-процессоре используется порядок байт little-endian; см. также [4]), иначе код статуса из нижележащего вызова usb_control_msg().

int usb_clear_halt (struct usb_device *dev, int pipe)

Укажет устройству очистить состояние halt/stall конечной точки.

Параметры:

struct usb_device *dev Устройство, конечная точка которого приостановлена.

int pipe Канал очищаемой конечной точки.

Контекст: контекст задачи, может быть sleep.

Описание:

Это используется для очистки halt состояний конечных точек bulk и interrupt, как сообщит статус завершения URB (completion status). Приостановленные конечные точки иногда называют "stalled". Такие конечные точки не могут передавать или принимать данные, пока не будет очищен halt status. Любые URB-ы, поставленные в очередь для такой конечной точки, должны быть нормально отсоединены (unlinked) драйвером перед очисткой halt condition, как описано в секциях 5.7.5 и 5.8.5 спецификации USB 2.0.

Обратите внимание, что конечные точки control и isochronous не могут попасть в halt, хотя репорт "protocol stall" конечных точек control (для неподдерживаемых запросов) использует тот же самый код статуса, который используется на истинном stall.

Это вызов синхронный, и не может использоваться в контексте прерывания. Если поток в вашем драйвере использует этот вызов, то необходимо обеспечить ожидание его завершения в методе disconnect().

Возвращаемое значение: 0 в случае успеха, или код статуса, возвращаемый нижележащим вызовом usb_control_msg().

void usb_reset_endpoint (struct usb_device *dev, unsigned int epaddr)

Сброс состояния контрольной точки.

Параметры:

struct usb_device *dev Устройство, конечная точка которого сбрасывается.

unsigned int epaddr Адрес конечной точки. Номер конечной точки для вывода, номер конечной точки + USB_DIR_IN для ввода.

Описание:

Сбросит любое состояние конечной точки на стороне хоста, такое как бит toggle, номер последовательности или текущее окно.

int usb_set_interface (struct usb_device *dev, int interface, int alternate)

Делает текущей определенную альтернативную настройку интерфейса.

Параметры:

struct usb_device *dev Устройство, интерфейс которой обновляется.

int interface Обновляемый интерфейс.

int alternate Выбранная настройка.

Контекст: контекст задачи, может быть sleep.

Описание:

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

В любой конфигурации каждый интерфейс может иметь несколько альтернативных настроек. Они часто используются для управления уровнями потребления полосы пропускания. Например, настройка по умолчанию для high speed interrupt конечной точки не может передавать более 64 байтов на микрофрейм, в то время как транзакции interrupt объемом до 3 килобайт на микрофрейм допустимы. Кроме того, изохронные конечные точки никогда не могут быть частью настроек интерфейса по умолчанию. Для доступа к такой полосе пропускания необходимо сделать текущей соответствующую альтернативную настройку для интерфейса.

Обратите внимание, что в Linux USB subsystem полоса пропускания, связанная с конечной точкой заданной альтернативной настройки, не резервируется до тех пор, пока не будет отправлен URB, необходимый для такой полосы. Некоторые другие операционные системы выделяют полосу заранее, при выборе конфигурации.

xHCI резервирует полосу и конфигурирует альтернативную настройку в usb_hcd_alloc_bandwidth(). В случае сбоя исходный альтернативный параметр интерфейса может быть отключен. Драйверы не могут полагаться на действие какого-либо конкретного альтернативного параметра после сбоя.

Это вызов синхронный, и не может использоваться в контексте прерывания. Также драйверы не должны менять альтернативные настройки, пока запланированы URB-ы для конечных точек этого интерфейса; все подобные URB-ы должны быть сначала завершены (возможно принудительно отключены, unlinking). Если поток вашем драйвере использует этот вызов, то его метод disconnect() должен быть способен дождаться окончания этого вызова.

Возвращаемое значение: 0 в случае успеха, или код статуса, возвращаемый нижележащим вызовом usb_control_msg().

int usb_reset_configuration (struct usb_device *dev)

Облегченный сброс устройства.

Параметры:

struct usb_device *dev Устройство, конфигурация которого сбрасывается.

Описание:

Выдаст стандартный запрос SET_CONFIGURATION в устройство, используя текущую конфигурацию. Эффект заключается в сбросе большинства состояний устройства, связанных с USB, включая альтернативные настройки (сброс в 0), endpoint halt-ы (очищаются), и endpoint state (только для конечных точек bulk и interrupt). Другое состояние usbcore не меняется, включая привязки к интерфейсам драйверов устройств USB.

Поскольку это влияет на несколько интерфейсов, избегайте использование этой функции с композитными (составными, или многоинтерфейсными) устройствами. Вместо этого драйвер для каждого интерфейса может использовать usb_set_interface() для заявляемых интерфейсов. Однако будьте осторожны: некоторые устройства не поддерживают запрос SET_INTERFACE, а другие не сбрасывают все состояние интерфейса (в частности состояние конечной точки). Сброс всей конфигурации повлияет на интерфейсы других драйверов.

Вызывающий код должен владеть блокировкой устройства (device lock).

Если этот вызов терпит неудачу, то устройство будет вероятно в неработоспособном состоянии с запрещенными конечными точками, и только частично разрешенными интерфейсами.

Возвращаемое значение: 0 в случае успеха, иначе отрицательный код ошибки.

int usb_set_wireless_status (struct usb_interface *iface, enum usb_wireless_status status)

Установит поле структуры wireless_status.

Параметры:

struct usb_interface *iface Модифицируемый интерфейс.

enum usb_wireless_status status Новое беспроводное состояние.

Описание:

Установит поле wireless_status структуры на новое значение, и выпустит изменения sysfs при необходимости.

Возвращаемое значение: 0 в случае успеха, -EALREADY если уже установлено.

int usb_driver_set_configuration (struct usb_device *udev, int config)

Предоставляет для драйверов способ поменять конфигурации устройства.

Параметры:

struct usb_device *udev Устройство, у которого обновляется конфигурация.

int config Выбранная конфигурация.

Контекст: контекст процесса, должен иметь возможность перейти в sleep.

Описание:

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

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

Возвращаемое значение: 0 если запрос успешно поставлен в очередь на выполнение, иначе код ошибки. У вызывающего кода нет возможности узнать, будет ли поставленный в очередь запрос в конечном итоге успешно выполнен.

int cdc_parse_cdc_header (struct usb_cdc_parsed_header *hdr,
                          struct usb_interface *intf,
                          u8 *buffer,
                          int buflen)

Парсит дополнительные заголовки (extra headers), присутствующие на устроствах CDC.

Параметры:

struct usb_cdc_parsed_header *hdr Место, куда будут помещены результаты парсинга.

struct usb_interface *intf Интерфейс, для которого запрашивается парсинг.

u8 *buffer Указатель на буфер для обрабатываемых extra заголовков.

int buflen Длина extra заголовков.

Описание:

Оцениваются дополнительные заголовки, присутствующие в устройствах CDC, которые связывают интерфейсы для данных и управления, а также предоставляют подробную информацию о возможностях устройства.

Возвращаемое значение: количество обработанных дескрипторов или -EINVAL, если заголовок противоречив и не подлежит восстановлению.

int usb_register_dev (struct usb_interface *intf,
                      struct usb_class_driver *class_driver)

Регистрирует устройство USB, и запрашивает minor-номер.

Параметры:

struct usb_interface *intf Указатель на usb_interface, который регистрируется.

struct usb_class_driver *class_driver Указатель на usb_class_driver для этого устройства.

Описание

Это должно быть вызвано всеми драйверами USB, которые используют USB major-номер. Если разрешен CONFIG_USB_DYNAMIC_MINORS, то minor-номер будет динамически выделяться из списка доступных. Если это не разрешено, то minor-номер будет основан на следующем доступном свободном minor, начиная от class_driver->minor_base.

Эта функция также создает устройство класса (usb class device) в дереве sysfs.

Должна быть вызвана usb_deregister_dev(), когда драйвер завершит работу с младшими номерами, выданными этой функцией.

Возвращаемое значение: -EINVAL, если что-то пошло не так с попыткой зарегистрировать устройство, и 0 при успехе.

void usb_deregister_dev (struct usb_interface *intf,
                         struct usb_class_driver *class_driver)

Отменяет регистрацию динамического minor для USB-устройства.

Параметры:

struct usb_interface *intf Указатель на usb_interface, у которого отменяется регистрация.

struct usb_class_driver *class_driver Указатель на usb_class_driver для этого устройства.

Описание:

Используется комплементарно с usb_register_dev(). Эта функция вызывается, когда драйвер USB завершил работу с номерами minor, полученными вызовом usb_register_dev() (обычно когда устройство отключается от системы).

Эта функция также удаляет класс устройства (usb class device) из дерева sysfs.

Это должно быть вызвано всеми драйверами, которые используют USB major-номер.

int usb_driver_claim_interface (struct usb_driver *driver,
                                struct usb_interface *iface,
                                void *data)

Привязывает драйвер к интерфейсу.

Параметры:

struct usb_driver *driver Драйвер для привязки.

struct usb_interface *iface Интерфейс, который будет привязан; должен быть в активной конфигурации устройства USB.

void *data Данные драйвера, связанные с этим интерфейсом.

Описание:

Это используется драйверами устройства USB, которым необходимо запрашивать более одного интерфейса на устройстве при зондировании (probing, например audio и acm). Драйверы устройств не должны напрямую изменять внутренние поля структуры usb_interface или usb_device.

Вызывающий код должен владеть блокировкой устройства (device lock), так что входы probe() драйвера не нуждаются в дополнительной блокировке, однако другие контексты вызова могут потребовать явно заявить о такой блокировке.

Возвращаемое значение: 0 в случае успеха.

void usb_driver_release_interface (struct usb_driver *driver,
                                   struct usb_interface *iface)

Отвязка (unbind) драйвера от интерфейса.

Параметры:

struct usb_driver *driver Отвязываемый драйвер.

struct usb_interface *iface Интерфейс, который будет отвязан.

Описание:

Это может использоваться драйварми для освобождения интерфейса без ожидания вызова их метода disconnect(). В типовых случаях это также приведет к вызов метода disconnect() драйвера.

Этот вызов синхронный, и не может использоваться в контексте прерывания. Вызывающий код должен владеть блокировкой устроства, так что входы в disconnect() драйвера не нуждаются в дополнительной блокировке, однако другие контексты вызова могут требовать явно заявить от такой блокировке.

const struct usb_device_id *usb_match_id (struct usb_interface *interface,
                                          const struct usb_device_id *id)

Найдет первый usb_device_id, соответствующий устройству или интерфейсу.

Параметры:

struct usb_interface *interface Интересующий интерфейс.

const struct usb_device_id *id Массив структур usb_device_id, завершенный нулевой записью.

Описание:

Функция usb_match_id ищет по массиву usb_device_id и возвратит первое совпавшее устройство или интерфейс, или NULL. Это используется при привязке (или при повторной привязке, rebinding) драйвера к интерфейсу. Большинство драйверов USB будут использовать это косвенно, через usb core, однако некоторые состоящие из слоев фреймворки драйверов используют это напрямую. Эти таблицы устройств экспортируются с MODULE_DEVICE_TABLE, через modutils, для поддержки функционала загрузки драйвера технологии USB hotplugging.

Что совпадает:

Элемент "match_flags" элемент в usb_device_id управляет, какие члены используются. Если установлен соответствующий бит, то значение в device_id должно совпадать с соответствующим членом в дескрипторе устройства или интерфейса, или иначе device_id не совпадет.

"driver_info" обычно используется только драйверами устройства, однако вы можете создать wildcard "совпадает с чем угодно" usb_device_id в качестве записи "modules.usbmap" драйвера, если предоставите id с только ненулевым полем "driver_info". Если вы сделали это, то подпрограмма probe() драйвера устройства USB должна использовать дополнительные аналитические данные для принятия решения о необходимости привязки к указанному интерфейсу.

Что делает таблицы usb_device_id хорошими:

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

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

Соответствия, основанные на спецификациях класса/подкласса/протокола устройства, несколько более общие; используйте макрос USB_DEVICE_INFO или его аналоги. Они используются для устройств с одной функцией, где bDeviceClass не указывает, что у каждого интерфейса есть свой класс.

Соответствия, основанные на классе/подклассе/протоколе интерфейса, являются наиболее общими; они позволяют драйверам подключаться к любому интерфейсу многофункционального устройства. Используйте макрос USB_INTERFACE_INFO или его аналоги для сопоставления устройств с классом на интерфейс (как записано в bInterfaceClass).

Обратите внимание, что запись, созданная USB_INTERFACE_INFO, не будет соответствовать ни одному интерфейсу, если класс устройства задан как Vendor-Specific. Это сделано намеренно: согласно спецификации USB, значения класса/подкласса/протокола интерфейса для этих устройств также зависят от поставщика, и следовательно, сопоставление со стандартным классом продукта в любом случае не будет работать. Если вы действительно хотите использовать сопоставление на основе интерфейса для такого устройства, создайте запись сопоставления, которая также указывает идентификатор поставщика (к сожалению, стандартного макроса для создания таких записей не существует).

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

Возвращаемое значение: первый совпавший usb_device_id, или NULL.

int usb_register_device_driver (struct usb_device_driver *new_udriver,
                                struct module *owner)

Регистрирует драйвер устройства USB (не интерфейса)

Параметры:

struct usb_device_driver *new_udriver Операции USB для драйвера устройства.

struct module *owner Владелец модуля этого драйвера.

Описание:

Регистрирует драйвер устройства USB с USB core. Список неподключённых устройств будет повторно сканироваться при каждом добавлении нового драйвера, что позволит новому драйверу подключаться к любым распознанным устройствам.

Возвращаемое значение: отрицательный код ошибки и 0 в случае успеха.

void usb_deregister_device_driver (struct usb_device_driver *udriver)

Отменяет регистрацию драйвера устройства USB (не интерфейса)

Параметры:

struct usb_device_driver *udriver Операции USB драйвера устройства для отмены регистрации.

Контекст: должен быть способен переходить в sleep.

Описание:

Отсоединяет указанный драйвер от внутреннего списка драйверов USB.

int usb_register_driver (struct usb_driver *new_driver,
                         struct module *owner,
                         const char *mod_name)

Регистрирует драйвер интерфейса USB.

Параметры:

struct usb_driver *new_driver Операции USB для драйвера интерфейса.

struct module *owner Владелец модуля этого драйвера.

const char *mod_name Строка имени модуля.

Описание:

Реристрирует драйвер интерфейса USB с USB core. Список неподключенных интерфейсов будет повторно сканироваться при каждом добавлении нового драйвера для подключения любых распознанных интерфейсов.

Возвращаемое значение: отрицательный код ошибки при отказе и 0 в случае успеха.

Замечание: если вы хотите, чтобы ваш драйвер использовал USB major номер, то вы должны вызывать usb_register_dev() для разрешения этого функционала. Функция usb_register_driver больше не заботится об этом.

void usb_deregister (struct usb_driver *driver)

Отменит регистрацию драйвера интерфейса USB.

Параметры:

struct usb_driver *driver Операции USB драйвера интерфейса, у которого отменяется регистрация.

Контекст: должен быть способен переходить в sleep.

Описание:

Отсоединяет указанный драйвер от внутреннего списка драйверов USB.

Замечание: если вы вызывали usb_register_dev(), то вам все еще нужно вызвать usb_deregister_dev() для очистки выделенных вашему драйверу minor номеров, этот * вызов больше не будет это делать за вас.

void usb_enable_autosuspend (struct usb_device *udev)

Разрешает автоматическую приостановку (autosuspend) устройства USB.

Параметры:

struct usb_device *udev Устройство USB, которое может автоматически приостанавливаться.

Описание:

Эта подпрограмма позволяет udev стать автоматически приостанавливаемыми. Autosuspend не произойдет, пока не истечет задержка autosuspend_delay и все другие необходимые условия не удовлетворены.

Вызывающий код должен удерживать блокировку устройства udev.

void usb_disable_autosuspend (struct usb_device *udev)

Предотвращает автоматическую приостановку (autosuspend) устройства USB.

Параметры:

struct usb_device *udev Устройство USB, которое не может автоматически приостанавливаться.

Описание:

Эта подпрограмма не даст udev автоматически приостановиться, и разбудит его если оно уже было autosuspended.

Вызывающий код должен удерживать блокировку устройства udev.

void usb_autopm_put_interface (struct usb_interface *intf)

Декрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру usb_interface, счетчик которого должен декрементироваться.

Описание:

Эта подпрограмма должна быть вызвана драйвером интерфейса, когда он завершил использование intf и хочет позволить его autosuspend. Типовой пример это драйвер символьного устройства, когда его файл устройства закрыт.

Эта подпрограмма декрементирует счетчик использования intf. Когда счетчик достигает 0, делается попытка вызвать отложенный запрос autosuspend для intf устройства. Эта попытка может окончиться неудачей (см. autosuspend_check()).

Эта подпрограмма может запускаться только в контексте процесса.

void usb_autopm_put_interface_async (struct usb_interface *intf)

Декрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру интерфейса.

Описание:

Эта подпрограмма делает почти то же самое, что и usb_autopm_put_interface(): декрементирует счетчик использования intf и планирует отложенный запрос на autosuspend, если счетчик меньше или равен 0. Отличие в том, что эта функция не выполняет никакую синхронизацию; вызывающий код должен удерживать private lock и сам обрабатывать все проблемы синхронизации.

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

Эта подпрограмма может быть запущена в атомарном контексте.

void usb_autopm_put_interface_no_suspend (struct usb_interface *intf)

Декрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру usb_interface, счетчик которого должен декрементироваться.

Описание:

Эта функция декрементирует счетчик использования intf, но не выполняет автоматическую приостановку (autosuspend).

Эта подпрограмма может быть запущена в атомарном контексте.

int usb_autopm_get_interface (struct usb_interface *intf)

Инкрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру usb_interface, счетчик которого должен инкрементироваться.

Описание:

Эта подпрограмма должна вызываться драйвером интерфейса, когда он хочет использовать intf и нуждается в гарантии, что он не приостановится. Кроме того, эта процедура предотвращает последующую автоматическую приостановку intf (обратите внимание, что это не предотвращает события приостановки, возникающие в ядре PM). Это предотвращение будет действовать до тех пор, пока не будет вызвана функция usb_autopm_put_interface() или не будет снята привязка intf. Типичным примером может служить драйвер символьного устройства при открытии его файла устройства.

Счетчик использования intf инкрементируется, предотвращая последующую автоматическую приостановку. Однако если autoresume терпит неудачу, то счетчик повторно декрементируется.

Эта подпрограмма может быть запущена только в контексте процесса.

Возвращаемое значение: 0 в случае успеха.

int usb_autopm_get_interface_async (struct usb_interface *intf)

Инкрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру usb_interface, счетчик которого должен инкрементироваться.

Описание:

Эта подпрограмма делает почти то же самое, что и usb_autopm_get_interface(): инкрементирует счетчик использования intf и ставит в очередь запрос autoresume, если устройство приостановлено. Отличие в том, что она не выполняет никакую синхронизацию (вызывающий код должен удерживать private lock и сам обрабатывать все проблемы синхронизации), и не выполняет autoresume устройства напрямую (только ставит в очередь запрос). После успешного вызова устройство может быть еще не возобновлено.

Эта подпрограмма может быть запущена в атомарном контексте.

Возвращаемое значение: 0 в случае успеха, иначе отрицательный код ошибки.

void usb_autopm_get_interface_no_resume (struct usb_interface *intf)

Инкрементирует счетчик использования PM-usage интерфейса USB.

Параметры:

struct usb_interface *intf Указатель на структуру usb_interface, счетчик которого должен инкрементироваться.

Описание:

Эта подпрограмма инкрементирует счетчик использования intf, но не осуществляет автовозобновление (autoresume).

Эта подпрограмма может быть запущена в атомарном контексте.

int usb_find_common_endpoints (struct usb_host_interface *alt,
                               struct usb_endpoint_descriptor **bulk_in,
                               struct usb_endpoint_descriptor **bulk_out,
                               struct usb_endpoint_descriptor **int_in,
                               struct usb_endpoint_descriptor **int_out)

Поиск общих дескрипторов конечных точек.

Параметры:

struct usb_host_interface *alt Альтернативная настройка для поиска.

struct usb_endpoint_descriptor **bulk_in Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **bulk_out Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **int_in Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **int_out Указатель на указатель дескриптора, или NULL.

Описание:

Поиск в дескрипторах конечных точек альтернативных настроек первых конечных точек bulk-in, bulk-out, interrupt-in и interrupt-out, и возврат из в предоставленных указателях (если только они не равны NULL).

Если запрошенная конечная точка не найдена, то соответствующий указатель устанавливается в NULL.

Возвращаемое значение: 0, если найдены все запрошенные дескрипторы, иначе -ENXIO.

int usb_find_common_endpoints_reverse (struct usb_host_interface *alt,
                                       struct usb_endpoint_descriptor **bulk_in,
                                       struct usb_endpoint_descriptor **bulk_out,
                                       struct usb_endpoint_descriptor **int_in,
                                       struct usb_endpoint_descriptor **int_out)

Поиск общих дескрипторов конечных точек.

Параметры:

struct usb_host_interface *alt Альтернативная настройка для поиска.

struct usb_endpoint_descriptor **bulk_in Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **bulk_out Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **int_in Указатель на указатель дескриптора, или NULL.

struct usb_endpoint_descriptor **int_out Указатель на указатель дескриптора, или NULL.

Описание:

Поиск в дескрипторах конечных точек альтернативных настроек последних конечных точек bulk-in, bulk-out, interrupt-in и interrupt-out, и возврат из в предоставленных указателях (если только они не равны NULL).

Если запрошенная конечная точка не найдена, то соответствующий указатель устанавливается в NULL.

Возвращаемое значение: 0, если найдены все запрошенные дескрипторы, иначе -ENXIO.

bool usb_check_bulk_endpoints (const struct usb_interface *intf,
                               const u8 *ep_addrs)

Проверит, содержит ли текущая альтернативная настройка (altsetting) интерфейса набор конечных точек bulk с заданными адресами.

Параметры:

const struct usb_interface *intf Интерфейс, текущие альтернативные настройки которого следует искать.

const u8 *ep_addrs Массив адресов конечных точек (номер и направление), завершающийся нулем, для поиска.

Описание:

Производит поиск конечных точек с указанными адресами и проверяет их типы.

Возвращаемое значение: true, если найдены все конечные точки и они bulk, иначе false.

bool usb_check_int_endpoints (const struct usb_interface *intf,
                              const u8 *ep_addrs)

Проверит, содержит ли текущая альтернативная настройка (altsetting) интерфейса набор конечных точек interrupt с заданными адресами.

Параметры:

const struct usb_interface *intf Интерфейс, текущие альтернативные настройки которого следует искать.

const u8 *ep_addrs Массив адресов конечных точек (номер и направление), завершающийся нулем, для поиска.

Описание:

Производит поиск конечных точек с указанными адресами и проверяет их типы.

Возвращаемое значение: true, если найдены все конечные точки и они interrupt, иначе false.

struct usb_host_interface *usb_find_alt_setting (struct usb_host_config *config,
                                                 unsigned int iface_num,
                                                 unsigned int alt_num)

Для заданной конфигурации найдет альтернативную настройку для заданного интерфейса.

Параметры:

struct usb_host_config *config Конфигурация для поиска (необязательно текущий config).

unsigned int iface_num Номер интерфейса для поиска.

unsigned int alt_num Номер альтернативной настройки интерфейса для поиска.

Описание:

Ищет кэш интерфейса конфигурации для заданного параметра alt.

Возвращаемое значение: альтернативная настройка, если она найдена, иначе NULL.

struct usb_interface *usb_ifnum_to_if (const struct usb_device *dev,
                                       unsigned ifnum)

Получит объект интерфейса с заданным номером интерфейса.

Параметры:

const struct usb_device *dev Устройство, текущая конфигурация которого рассматривается.

unsigned ifnum Желаемый интерфейс.

Описание:

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

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

Не вызывайте эту функцию, если вы не привязаны к одному из интерфейсов на этом устройстве или не заблокировали устройство!

Возвращаемое значение: указатель на интерфейс, имеющий ifnum в качестве номера интерфейса, если найден. В противном случае возвратит NULL.

struct usb_host_interface *usb_altnum_to_altsetting (const struct usb_interface *intf,
                                                     unsigned int altnum)

Получить структуру альтернативной настройки (altsetting) с заданным номером альтернативной настройки.

Параметры:

const struct usb_interface *intf Интерфейс, содержащий рассматриваемую altsetting.

unsigned int altnum Номер желаемой альтернативной настройки.

Описание:

Выполняет поиск по массиву альтернативных настроек указанного интерфейса элемента, у которого корректное значение bAlternateSetting.

Обратите внимание, что altsettings не обязательно хранить последовательно по номерам, поэтому было бы неверно предполагать, что первый элемент altsetting в массиве соответствует altsetting 0. Эта процедура помогает драйверам устройств избегать подобных ошибок.

Не вызывайте эту функцию, если вы не привязаны к интерфейсу intf или не заблокировали устройство!

Возвращаемое значение: указатель на элемент массива altsetting интерфейса intf, у которого altnum в качестве номера альтернативной настройки. Если элемент не найден, то возвратит NULL.

struct usb_interface *usb_find_interface (struct usb_driver *drv, int minor)

Ищет указатель usb_interface для драйвера и устройства.

Параметры:

struct usb_driver *drv Драйвер, у которого рассматривается текущая конфигурация.

int minor Номер minor желаемого устройства.

Описание:

Просматривает список устройств шины и возвращает указатель на интерфейс с соответствующим номером minor и драйвером. Обратите внимание: это работает только для устройств с одинаковым major номером USB.

Возвращаемое значение: указатель на интерфейс с совпавшими major и minor номерами.

int usb_for_each_dev (void *data, int (*fn)(struct usb_device*, void*))

Итерация по всем устройствам USB в системе.

Параметры:

void *data Указатель на данные, который будет передан функции обратного вызова.

int (*fn)(struct usb_device *, void *) Функция обратного вызова, которая будет вызвана для каждого устройства USB.

Описание:

Итерация по всем устройствам USB и вызов функции fn для каждого, с передачей в эту функцию данных data. Если если функция возвратит что-либо, отличное от 0, то преждевременно прерывается итерация и возвращается это значение.

struct usb_device *usb_alloc_dev (struct usb_device *parent,
                                  struct usb_bus *bus,
                                  unsigned port1)

Конструктор устройства USB (внутренний usbcore).

Параметры:

struct usb_device *parent Хаб, к которому подключено устройство; null для выделения root hub.

struct usb_bus *bus Шина, используемая для доступа к устройству.

unsigned port1 Индекс порта по базе 1. Игнорируется для root хабов.

Контекст: контекст задачи, может переходить в sleep.

Описание:

Вызывать этот метод следует только драйверам концентраторов (включая драйверы виртуального корневого концентратора для хост-контроллеров).

Этот вызов не может использоваться в non-sleeping контексте.

Возвращаемое значение: при успехе указатель на выделенное устройство USB. NULL при ошибке.

struct usb_device *usb_get_dev (struct usb_device *dev)

Увеличивает счетчик ссылок структуры USB-устройства.

Параметры:

struct usb_device *dev Устройство, на которое ссылаются.

Описание:

Каждая живая ссылка на устройство должна быть учтена.

Драйверы для USB-интерфейсов обычно должны регистрировать такие ссылки в своих методах probe() при привязке к интерфейсу и освобождать их, вызывая usb_put_dev() в своих методах disconnect(). Однако, если драйвер не обращается к структуре usb_device после завершения работы метода disconnect(), подсчёт ссылок не требуется, поскольку ядро ​​USB гарантирует, что usb_device не будет освобождёно до тех пор, пока не будут отвязаны все драйверы его интерфейсов.

Возвращаемое значение: указатель на устройство с инкрементированным счетчиком ссылок.

void usb_put_dev (struct usb_device *dev)

Освободит использование структуры USB-устройства.

Параметры:

struct usb_device *dev Устройство, которое было отключено.

Описание:

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

struct usb_interface *usb_get_intf (struct usb_interface *intf)

Увеличивает счетчик ссылок структуры интерфейса USB.

Параметры:

struct usb_interface *intf Интерфейс, на который ссылаются.

Описание:

Каждая живая ссылка на интерфейс должна быть учтена.

Драйверы для USB-интерфейсов обычно должны регистрировать такие ссылки в своих методах probe() при привязке к интерфейсу и освобождать их, вызывая usb_put_intf() в своих методах disconnect(). Однако, если драйвер не обращается к структуре usb_interface после завершения работы метода disconnect(), подсчёт ссылок не требуется, поскольку ядро ​​USB гарантирует, что usb_interface не будет освобождён до тех пор, пока не будет отвязан его драйвер.

Возвращаемое значение: указатель на интерфейс с инкрементированным счетчиком ссылок.

void usb_put_intf (struct usb_interface *intf)

Освобождает использование структуры интерфейса USB.

Параметры:

struct usb_interface *intf Интерфейс, который декрементируется.

Описание:

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

struct device *usb_intf_get_dma_device (struct usb_interface *intf)

Получит ссылку на конечную точку DMA интерфейса USB.

Параметры:

struct usb_interface *intf Интерфейс USB.

Описание:

Хотя USB-устройство не может выполнять операции DMA самостоятельно, многие USB-контроллеры могут. Вызов функции usb_intf_get_dma_device() возвращает конечную точку DMA для заданного USB-интерфейса, если таковая имеется. Возвращённую структуру устройства необходимо освободить с помощью функции put_device().

См. также usb_get_dma_device().

Возвращаемое значение: ссылка на конечную точку DMS интерфейса USB, или NULL если таковой нет.

int usb_lock_device_for_reset (struct usb_device *udev,
                               const struct usb_interface *iface)

Осторожно захватывает блокировку для структуры устройства USB.

Параметры:

struct usb_device *udev Устройство, которое блокируется.

const struct usb_interface *iface Интерфейс, привязанный к драйверу, выполняющему запрос (необязательно).

Описание:

Пытается получить блокировку устройства, но терпит неудачу, если устройство NOTATTACHED (не подсоединено) или SUSPENDED (приостановлено), или указанный iface и интерфейс ни в состоянии BINDING, ни в состоянии BOUND. Вместо ухода в сон для ожидания блокировки, подпрограмма выполняет постоянный опрос. Это необходимо для предотвращения взаимоблокировки (deadlock) при отключении; в некоторых драйверах (например, usb-storage) метод disconnect() или suspend() блокирует ожидание завершения сброса устройства.

Возвращаемое значение: отрицательный код ошибки в случае неудачи, иначе 0.

int usb_get_current_frame_number (struct usb_device *dev)

Возвратит номер текущего кадра шины.

Параметры:

struct usb_device *dev Устройство, шина которого опрашивается.

Возвращаемое значение:

Номер текущего кадра для контроллера хоста USB, используемый с указанным устройством USB. Это может использоваться при планированнии изохронных запросов.

Замечание: различные виды хост-контроллеров имеют разные "горизонты планирования". В то время как один может поддерживать до 32 кадров на будущее, другой может поддерживать до 1024 кадров на будущее.

void *usb_alloc_coherent (struct usb_device *dev,
                          size_t size,
                          gfp_t mem_flags,
                          dma_addr_t *dma)

Выделяет подходящий для прямого доступа к памяти (DMA-consistent) буфер для URB_NO_xxx_DMA_MAP.

Параметры:

struct usb_device *dev Устройство, с которым используется буфер.

size_t size Запрашиваемый размер буфера.

gfp_t mem_flags Влияют на то, что может ли выделение блокировать.

dma_addr_t *dma Используется для возврата адреса DMA буфера.

Возвращаемое значение: либо NULL (показывает, что буфер не может быть выделен), или указатель на буфер пространства CPU, который может использоваться для выполнения DMA на указанном устройстве. Такие буферы пространства CPU возвращаются вместе с адресом DMA (через предоставленный указатель).

Замечание: эти буферы используются с URB_NO_xxx_DMA_MAP, установленным в urb->transfer_flags, чтобы избежать поведения наподобие "DMA bounce buffers", или перегрузки оборудования IOMMU во время завершения/повторной отправки URB. Реализация различается на разных платформах в зависимости от особенностей работы DMA с данным устройством. Использование этих буферов также устраняет проблемы совместного использования строк кэша на архитектурах, где кэши процессора не когерентны с DMA. В системах без bus-snooping кэшей отслеживания шины эти буферы не кэшируются.

Когда буфер больше не используется, освободите его вызовом usb_free_coherent().

void usb_free_coherent (struct usb_device *dev,
                        size_t size,
                        void *addr,

                        dma_addr_t dma)

Освободит память, выделенную вызовом usb_alloc_coherent().

Параметры:

struct usb_device *dev Устройство, с которым использовался буфер.

size_t size Запрошенный размер буфера.

void *addr Адрес CPU буфера.

dma_addr_t dma Адрес DMA буфера.

Описание:

Освобождает буфер ввода-вывода, позволяя использовать его повторно. Память должна быть выделена с помощью функции usb_alloc_coherent(), а параметры должны соответствовать параметрам, указанным в запросе на выделение.

void *usb_alloc_noncoherent (struct usb_device *dev,
                             size_t size,
                             gfp_t mem_flags,
                             dma_addr_t *dma,
                             enum dma_data_direction dir,
                             struct sg_table **table)

Выделяет некогерентный для DMA буфер для URB_NO_xxx_DMA_MAP.

Параметры:

struct usb_device *dev Устройство, с которым будет использоваться буфер.

size_t size Запрошенный размер буфера.

gfp_t mem_flags Влияют на то, что может ли выделение блокировать.

dma_addr_t *dma Используется для возврата DMA адреса буфера.

enum dma_data_direction dir Направление транзакции DMA.

struct sg_table **table Используется для возврата sg_table выделенной памяти.

Описание:

Для явного управления распределением памяти между ядром и устройством через USB-ядро пользователю необходимо сохранить sg_table в urb->sgt. После этого USB-ядро будет корректно выполнять DMA-синхронизацию между CPU и устройством.

Когда буфер больше не используется, освободите его вызовом usb_free_noncoherent().

Возвращаемое значение: либо NULL (показывает, что буфер не может быть выделен), или указатель на буфер пространства CPU, который может использоваться для выполнения DMA на указанном устройстве. Такие буферы пространства CPU возвращаются вместе с адресом DMA (через предоставленный указатель).

void usb_free_noncoherent (struct usb_device *dev,
                           size_t size,
                           void *addr,
                           enum dma_data_direction dir,
                           struct sg_table *table)

Освободит память, выделенную через usb_alloc_noncoherent().

Параметры:

struct usb_device *dev Устройство, с которым использовался буфер.

size_t size Запрошенный размер буфера.

void *addr CPU адрес буфера.

enum dma_data_direction dir Направление транзакции DMA.

struct sg_table *table Описывает выделенную и отображенную на DMA память.

Описание:

Освобождает буфер ввода/вывода I/O, позволяя выделенную память использовать повторно для других целей. Эта освобождаемая память должна быть ранее выделена вызовом usb_alloc_noncoherent(), и параметры должны соответствовать тем, что использовались в запросе на выделение.

Это ключевые понятия в программировании драйверов Linux, особенно когда дело доходит до высокопроизводительных устройств.

Давайте разберем оба типа буферов по порядку.

Coherent DMA Buffer (согласованный): буфер, в котором и ЦПУ, и устройство всегда видят одни и те же данные. Специальное аппаратное обеспечение (кэш-когерентность) или программное обеспечение (кэш-инвалидация) гарантирует, что кэши ЦПУ и содержимое памяти синхронизированы. Это "просто работает", но может быть медленнее.

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

[Coherent DMA Buffer (aka "Consistent DMA")]

Аналогия: gредставьте себе общий онлайн-документ (например, Google Docs), где все участники (ЦПУ и устройство) всегда видят самую последнюю версию. Любое изменение мгновенно становится видно всем.

Принцип работы:

● Ядро выделяет специальную область памяти, которая по умолчанию не кэшируется или работает в когерентном кэше.
● Благодаря этому, любая запись со стороны ЦПУ немедленно видна устройству, и наоборот.
● Нет необходимости вручную управлять кэшем.

Когда использовать:

● Для небольших структур данных, которые часто меняются и с той, и с другой стороны (например, кольцевые буферы команд или состояний).
● Когда устройство и ЦПУ активно обмениваются данными без четкого разделения на "отправку" и "получение".

API в Linux:

dma_alloc_coherent()
dma_free_coherent()

Плюсы:

● Простота: yе нужно задумываться о синхронизации кэша.
● Надежность: bсключает целый класс ошибок, связанных с рассинхронизацией кэша.

Минусы:

● Производительность: jтсутствие кэширования для ЦПУ может сильно замедлить последовательный доступ к данным.
● Ограничения: fрхитектура может накладывать ограничения на размер и расположение такой памяти.

[Noncoherent DMA Buffer]

Аналогия: представьте, что вы и ваш коллега работаете с локальными копиями документа. Вы вносите изменения в свою копию (кэш ЦПУ), а он — в свою (память устройства). Чтобы увидеть изменения друг друга, вы должны явно отправить свою копию по почте (DMA_TO_DEVICE) или скачать свежую копию от коллеги (DMA_FROM_DEVICE).

Принцип работы:

● Буфер выделяется как обычная память, которая кэшируется ЦПУ для скорости.

● Это означает, что данные в оперативной памяти могут быть устаревшими по сравнению с кэшем ЦПУ, и наоборот.

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

   - Перед тем, как устройство начнет читать данные из RAM (после того как ЦПУ их туда записал), нужно "сбросить" (flush) кэш ЦПУ в основную память. Это гарантирует, что устройство увидит самые свежие данные. Направление: DMA_TO_DEVICE.
   - После того, как устройство записало данные в RAM (и перед тем как ЦПУ начнет их читать), нужно "признать недействительным" (invalidate) кэш ЦПУ. Это гарантирует, что ЦПУ получит данные из памяти, а не устаревшую копию из своего кэша. Направление: DMA_FROM_DEVICE.
   - DMA_BIDIRECTIONAL включает оба сценария.

Когда использовать:

● Для передачи больших объемов данных (например, сетевых пакетов, блоков данных для диска), где есть четкие этапы: "ЦПУ подготовил -> отдал устройству" или "устройство получило -> отдало ЦПУ".

API в Linux:

● Выделение: не имеет специального API, это может быть обычный kmalloc, или память, выделенная в пользовательском пространстве, или любая другая.
● Синхронизация: dma_sync_single_for_device(), dma_sync_single_for_cpu(), dma_map_single(), dma_unmap_single().

Плюсы:

● Высокая производительность: ЦПУ может работать с данными в быстром кэше до момента синхронизации.
● Гибкость: можно использовать любую обычную память.

Минусы:

● Сложность: легко допустить ошибку, забыв синхронизировать кэш, что приведет к тонким и трудноотлавливаемым багам.

Сравнительная таблица:

Характеристика Coherent DMA Noncoherent DMA
Синхронизация кэша Автоматическая Ручная (программистом)
Производительность Ниже (часто без кэша) Выше (с кэшированием)
Простота использования Высокая Низкая (легко ошибиться)
Типичное использование Кольцевые буферы, структуры состояний Большие блоки данных (пакеты, файлы)
API выделения dma_alloc_coherent() Любое (например, kmalloc)
API синхронизации Не требуется dma_sync_single_*(), dma_map_*()

Итог: выбор между coherent и noncoherent DMA — классический компромисс между производительностью и простотой.

● Используйте Coherent DMA для небольших, часто изменяемых контрольных структур, где важна надежность и простота кода.
● Используйте Noncoherent DMA для больших передач данных, где производительность критична, и вы готовы взять на себя ответственность за правильную синхронизацию кэша.

u32 usb_endpoint_max_periodic_payload (struct usb_device *udev,
                                       const struct usb_host_endpoint *ep)

Получит максимальное количество байт полезной нагрузки на интервал обслуживания.

Параметры:

struct usb_device *udev Устройство USB.

const struct usb_host_endpoint *ep Конечная точка.

Возвращаемое значение: максимальное количество байт isochronous конечной точки или конечной точки interrupt, которое можно передать на интервале обслуживания, или 0 для других конечных точек.

bool usb_endpoint_is_hs_isoc_double (struct usb_device *udev,
                                     const struct usb_host_endpoint *ep)

Определит, использует ли конечная точка USB 2 Isochronous Double IN Bandwidth.

Параметры:

struct usb_device *udev Устройство USB.

const struct usb_host_endpoint *ep Конечная точка.

Возвращаемое значение: true, если конечная точка удовлетворяет USB 2 Isochronous Double IN Bandwidth ECN, иначе false.

Устройство с конечной точкой, соответствующей спецификации USB 2 Isochronous Double IN Bandwidth ECN, обладает ключевым улучшением: оно может передавать данные в два раза больше по сравнению с обычным устройством USB 2.0, сохраняя при этом совместимость с архитектурой USB 2.0.

Примечание: ECN расшифровывается как "Engineering Change Notice", т. е. "уведомление об инженерном изменении".

Это расширение стандарта, разработанное для встроенных (embedded) USB-устройств, таких как веб-камеры следующего поколения.

● Основная цель: устранить узкое место производительности USB 2.0 для устройств, требующих высокой пропускной способности (например, видео), без перехода на более дорогой и сложный USB 3.x.
● Как идентифицировать: устройство с этой функцией в дескрипторе будет иметь значение `bcdUSB 0x0220` и специальный eUSB2 Isochronous Endpoint Companion Descriptor.
● Ключевой механизм: в обычном дескрипторе конечной точки поле `wMaxPacketSize` может быть равно нулю, а реальный максимальный размер пакета и увеличенная пропускная способность определяются через вспомогательный дескриптор.

[Сравнение с обычным USB 2.0]

В таблице ниже показаны ключевые отличия для изохронных передач данных:

Характеристика Обычный USB 2.0 eUSB2 с двойной полосой
Пропускная способность Стандартная (до ~102.4 Мбит/с для полезной нагрузки) Вдвое больше стандартной
Максимальный размер пакета Указывается в wMaxPacketSize Определяется через eUSB2 Isochronous Endpoint Companion Descriptor
Идентификация bcdUSB 0x0200 bcdUSB 0x0220

[Почему это важно: поддержка в системе]

Для работы такого устройства необходима комплексная поддержка на стороне хоста (вашего компьютера):

1. Ядро ОС: драйверы USB-ядра должны уметь анализировать новый вспомогательный дескриптор.
2. Драйвер устройства: специфичные драйверы (например, uvcvideo для камер) должны использовать новые API для корректного расчета размера URB (блоков запросов USB).
3. Хост-контроллер: драйвер хост-контроллера (например, xhci) должен правильно запрограммировать оборудование, чтобы использовать удвоенную полосу пропускания.

Например, поддержка в Linux была добавлена в драйверы UVC, USB-ядро и xHCI.

Итог: соответствие USB 2 Isochronous Double IN Bandwidth ECN означает, что ваше USB-устройство использует современный стандарт для встроенных систем, который удваивает эффективную пропускную способность для изохронного трафика, сохраняя преимущества простой и проверенной архитектуры USB 2.0.

int usb_hub_clear_tt_buffer (struct urb *urb)

Очистит control/bulk TT state в high speed хабе.

Параметры:

struct urb *urb URB, связанный с ошибочной или незавершенной split-транзакцией.

Описание:

Драйверы high speed HCD используют это, чтобы сказать драйверу хаба, что какая-то разделенная транзакция управления (split control transaction) или bulk-транзакция потерпела неудачу и нуждается в очистке внутреннего состояния транслятора транзакции. Это нормально определяется (и об этом сообщается) из контекста прерывания.

Может быть невозможным для хаба обработать дополнительные full speed (или low speed) транзакции, пока такое состояние не будет полностью очищено.

Возвращаемое значение: 0 в случае успеха, иначе отрицательный код ошибки.

}

Транслятор транзакций (TT) это специальный компонент в USB 2.0 высокоскоростном (High-Speed, HS) хабе, который обеспечивает совместимость между высокоскоростными устройствами и низкоскоростными (Low-Speed, LS)/полноскоростными (Full-Speed, FS) устройствами.

Как работает TT:

1. Преобразование скоростей:

   - Высокоскоростная шина (480 Мбит/с) ↔ Низкоскоростная (1.5 Мбит/с) или Полноскоростная (12 Мбит/с)

2. Буферизация транзакций:

   - Накопление нескольких медленных транзакций от LS/FS устройств.
   - "Упаковка" их для эффективной передачи по высокоскоростной шине

3. Управление потоком:

   - Согласование разных таймингов и протоколов передачи

[Почему важна очистка TT буфера]

Функция usb_hub_clear_tt_buffer() очищает состояние TT после завершения URB (USB Request Block), потому что:

- Control/Bulk транзакции могут занимать буферы TT
- При ошибках передачи или отмене URB буферы могут остаться занятыми
- Очистка гарантирует, что последующие транзакции не будут конфликтовать с предыдущими

Типичный сценарий:

High-Speed Host
    ↓ (480 Мбит/с)
High-Speed Hub с TT
    ↓ (12 Мбит/с или 1.5 Мбит/с)
Full-Speed/Low-Speed Device

TT позволяет подключать старые USB 1.1 устройства (мыши, клавиатуры, модемы) к современным высокоскоростным USB 2.0/3.0 портам без потери функциональности.

Поэтому функция usb_hub_clear_tt_buffer() обеспечивает корректную работу с низкоскоростными устройствами через высокоскоростные USB хабы.

void usb_set_device_state (struct usb_device *udev,
                           enum usb_device_state new_state)

Изменение текущего состояния устройства (usbcore, hcds).

Параметры:

struct usb_device *udev Указатель на устройство, состояние которого должно быть изменено.

enum usb_device_state new_state Значение нового сохраняемого состояния.

Описание:

udev->state НЕПОЛНОСТЬЮ защищено блокировкой устройства. Хотя большинство транзакций выполняются только при удержании блокировки, состояние может поменяться на USB_STATE_NOTATTACHED почти в любое время. Это делается для того, чтобы устройства могли быть помечены как отключенные как можно скорее, не дожидаясь освобождения каких-либо семафоров. В результате все изменения состояния любого устройства должны быть защищены device_ state_lock spinlock.

Как только устройство было добавлено в дерево устройств, все изменения его состояния должны быть сделаны через эту подпрограмму. Состояние НЕ устанавливается напрямую.

Если состояние udev->state уже USB_STATE_NOTATTACHED, то изменение не делается. Иначе udev->state установится в новое состояние, и если новое состояние USB_STATE_NOTATTACHED, то состояния всех потомков udev также поменяются на USB_STATE_NOTATTACHED.

void usb_root_hub_lost_power (struct usb_device *rhdev)

Вызывается драйвером HCD, если корневой хаб (root hub) потерял питание Vbus.

Параметры:

struct usb_device *rhdev Структура usb_device для корневого хаба.

Описание:

Драйвер хост-контролера USB вызовет эту функцию, когда его root hub возобновил работу, и питание Vbus было прервано или контроллер был сброшен. Эта подпрограмма пометит rhdev как потерявший питание. Когда драйвер хаба возобновится, он примет это к сведению и проведет сессию восстановления питания для всех "USB-PERSIST"-разрешенных дочерних устройств; все другие будут отключены.

int usb_reset_device (struct usb_device *udev)

Предупреждает драйверы интерфейса и выполняет сброс порта USB.

Параметры:

struct usb_device *udev Устройство для сброса (не в состоянии NOTATTACHED).

Описание:

Предупреждает все драйверы, привязанные к зарегистрированным интерфейсам (с использованием их метода pre_reset), выполняет сброс порта, и затем дает драйверам понять, что сброс завершился (с использованием их метода post_reset).

Возвращаемое значение: то же самое, как для usb_reset_and_verify_device(). Однако если сброс уже выполняется (например, если у драйвера нет функций обратного вызова pre_reset() или post_reset(), и во время отвязывания (unbound) или повторной привязки (re-bound) во время выполняющегося сброса его подпрограмма disconnect() или probe() пытается выполнить второй, вложенный сброс), то подпрограмма вернет -EINPROGRESS.

Замечание: вызывающий код должен владеть блокировкой устройства. Например, это можно безопасно использовать из подпрограммы probe() драйвера после загрузки нового firmware. Для вызовов, которые не могут произойти во время probe(), драйверы должны блокировать устройство с помощью usb_lock_device_for_reset().

Если интерфейс в настоящий момент находится под probe или отключен, то подразумевается, что его драйвер знает как обработать сброс. Для всех других интерфейсов, если у драйвера нет методов pre_reset и post_reset, то мы пытаемся отменить его привязку и повторите привязку после.

void usb_queue_reset_device (struct usb_interface *iface)

Сброс устройства USB из атомарного контекста.

Параметры:

struct usb_interface *iface Интерфейс USB, принадлежащий устройству для сброса.

Описание:

Эта функция может использоваться для сброса устройства USB из атомарного контекста, где usb_reset_device() не будет работать (из-за её блокировки).

Выполнение сброса с использованием usb_queue_reset_device функционально эквивалентно вызову usb_reset_device(), за исключением того факта, что это откладывается на рабочую очередь. Это означает, что любые драйверы, привязанные к другим интерфейсам, могут быть отвязаны, как и пользователи из usbfs в user space.

Предельные случаи:

● Возможно планирование двух сбросов одновременно из двух разных драйверов, присоединенных к двум разным интерфейсам одного и того же устройства; в зависимости от того, как драйвер, подключенный к каждому интерфейсу, обрабатывает ->pre_reset(), второй сброс может произойти или не произойти.
● Если сброс задерживается настолько, что интерфейс отсоединяется от своего драйвера, сброс будет пропущен.
● Эту функцию можно вызвать во время .probe(). Её также можно вызвать во время .disconnect(), но это бессмысленно, так как сброса не произойдёт. Если вы действительно хотите сбросить устройство во время .disconnect(), вызовите usb_reset_device() напрямую, но будьте осторожны с вложенными проблемами отмены привязки!

struct usb_device *usb_hub_find_child (struct usb_device *hdev,
                                       int port1)

Получит указатель дочернего устройства, подключенного к порту, который указан через port1.

Параметры:

struct usb_device *hdev Устройство USB, принадлежащее хабу USB.

int port1 Номер порта для индикации, к какому порту подключено дочернее устройство.

Описание:

Драйверы USB вызывают эту функцию, чтобы получить указатель на дочернее устройство хаба.

Возвращаемое значение: NULL, если входной параметр недопустимый, и ненулевой указатель на дочернее usb_device.

[Host Controller API]

Эти API-фунции предназначены только для использования драйверами контроллера хоста (host controller drivers, HCD), большниство из которых реализуют стандатные интерфейсы регистров, такие как XHCI, EHCI, OHCI, или UHCI. UHCI был одним из первых интерфейсов, разработанных Intel, и также используемых VIA; он не выполняет большой аппаратной нагрузки. Интерфейс OHCI был разработан позже, чтобы аппаратное обеспечение выполняло больше работы (передача больших объёмов данных, отслеживание состояния протокола и т. д.). Интерфейс EHCI был разработан с использованием USB 2.0; его архитектура имеет функции, схожие с OHCI (аппаратное обеспечение выполняет гораздо больше работы), а также с UHCI (некоторые аспекты поддержки ISO, обработка списка TD). Интерфейс XHCI был разработан с использованием USB 3.0. Он продолжает переносить поддержку функциональности на аппаратное обеспечение.

Существуют и другие хост-контроллеры, помимо "большой тройки", хотя большинство контроллеров на базе PCI (и несколько контроллеров, не основанных на PCI) используют один из этих интерфейсов. Не все хост-контроллеры используют DMA; некоторые используют PIO, а также существуют симулятор и виртуальный хост-контроллер для передачи USB по сети.

Драйверам всех этих контроллеров доступны одни и те же базовые API. Исторически они разделены на два уровня: struct usb_bus — довольно тонкий уровень, появившийся в ядрах версии 2.2, а struct usb_hcd — более функциональный уровень, позволяющий HCD использовать общий код, что позволяет уменьшить размер драйвера и значительно снизить специфичное для HCD поведение.

long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)

Апроксимирует время периодических транзакций в наносекундах.

Параметры:

int speed Из dev->speed; USB_SPEED_{LOW,FULL,HIGH}.

int is_input true, если транзакция посылает данные в хост.

int isoc true для изохронных транзакций, false для interrupt транзакций.

int bytecount Сколько байт в транзакции.

Возвращаемое значение: апроксимирует время шины в наносекундах для периодических транзакций.

Замечание: см. секцию 5.11.3 спецификации USB 2.0; только периодические транзакции должны планироваться программно, эта функция нужна для такого планирования.

int usb_hcd_link_urb_to_ep (struct usb_hcd *hcd, struct urb *urb)

Добавит URB в очередь конечной точки.

Параметры:

struct usb_hcd *hcd Хост-контроллер, которому предоставляется URB.

struct urb *urb Предоставляемый URB.

Описание:

Драйверы HCD должны вызвать эту функцию в своем методе enqueue(). Необходимо удерживать приватную спин-блокировку HCD и отключать прерывания. Выполняемые здесь действия необходимы для отправки URB, а также для завершения работы конечной точки и для usb_kill_urb.

Возвращаемое значение: 0 при отсутствии ошибки, иначе отрицательный код ошибки (в этом случае метод enqueue() должен завершиться неудачей). Если ошибки не происходит, но enqueue() всё равно завершается неудачей, необходимо вызвать usb_hcd_unlink_urb_from_ep() перед снятием приватной спин-блокировки и возвратом.

int usb_hcd_check_unlink_urb (struct usb_hcd *hcd,
                              struct urb *urb,
                              int status)

Проверит, можно ли отсоединить URB.

Параметры:

struct usb_hcd *hcd Хост-контролер, которому был предоставлен URB.

struct urb *urb URB, проверяемый на возможность отсоединения.

int status Код ошибки для сохранения в URB, если unlink успешен.

Описание:

Драйверы HCD должны вызывать эту функцию в своем методе dequeue(). Выполняемые здесь действия необходимы для проверки корректности разрыва связи (unlink).

Возвращаемое значение: 0 при отсутствии ошибки, иначе отрицательный код ошибки (в этом случае метод dequeue() должен завершиться неудачей). Возможные коды ошибки:

-EIDRM: URB не был предоставлен или уже был завершен. Функция завершения могла быть еще не вызвана.
-EBUSY: URB был уже отсоединен.

void usb_hcd_unlink_urb_from_ep (struct usb_hcd *hcd, struct urb *urb)

Удалит URB из очереди конечной точки.

Параметры:

struct usb_hcd *hcd Хост-контролер, которому был предоставлен URB.

struct urb *urb Отсоединяемый URB.

Описание:

Драйверы HCD должны вызвать эту подпрограмму перед вызовом usb_hcd_giveback_urb(). Необходимо удерживать приватную спин-блокировку HCD и отключать прерывания. Выполняемые здесь действия необходимы для завершения URB.

void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, int status)

Возвратит URB из HCD для драйвера устройства.

Параметры:

struct usb_hcd *hcd Хост-контроллер, возвращающий URB.

struct urb *urb URB, возвращаемый в драйвер устройства USB.

int status Код статуса завершения для URB.

Контекст: атомарный. Callback-функция завершения вовлекается либо в контексте рабочей очереди (BH), либо в контексте вызывающего кода, в зависимости от того, был ли установлен флаг HCD_BH в структуре hcd, за исключением ситуации, когда URB-ы предоставлены корневому хабу, что всегда завершаются в контексте BH.

Описание:

Этот код передаёт URB из HCD в драйвер USB-устройства, используя его функцию завершения. HCD освободил все ресурсы, выделенные для каждого urb (что выполняется с помощью urb->hcpriv). Он также снял все блокировки HCD; драйвер устройства не создаст проблем, если освободит, изменит или повторно отправит этот URB.

Если urb был отсоединён, значение status будет переопределено значением urb->unlinked. Ошибочные короткие передачи обнаруживаются, если HCD их не проверил.

int usb_alloc_streams (struct usb_interface *interface,
                       struct usb_host_endpoint **eps,
                       unsigned int num_eps,
                       unsigned int num_streams,
                       gfp_t mem_flags)

Выделяет идентификаторы потока конечной точки bulk (bulk endpoint stream ID).

Параметры:

struct usb_interface *interface Альтернативная настройка, которая включает все конечные точки.

struct usb_host_endpoint **eps Массив конечных точек, которым нужны потоки.

unsigned int num_eps Количество конечных точек в массиве.

unsigned int num_streams Количество потоков для выделения.

gfp_t mem_flags Флаги, которые HCD должен использовать для выделения памяти.

Описание:

Настраивает группу bulk конечных точек, чтобы было доступно num_streams идентификаторов потоков. Драйверы могут ставить в очередь несколько передач к разным идентификаторам потоков, которые могут выполняться в порядке, отличном от того, в котором они были поставлены в очередь.

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

int usb_free_streams (struct usb_interface *interface,
                      struct usb_host_endpoint **eps,
                      unsigned int num_eps,
                      gfp_t mem_flags)

Освобождает идентификаторы потока конечной точки bulk (bulk endpoint stream ID).

Параметры:

struct usb_interface *interface Альтернативная настройка, которая включает все конечные точки.

struct usb_host_endpoint **eps Массив конечных точек, из которых удаляются потоки.

unsigned int num_eps Количество конечных точек в массиве.

gfp_t mem_flags Флаги, которые HCD должен использовать для выделения памяти.

Описание:

Отключает использование идентификаторов потока для группы bulk коненых точек. Может завершиться неудачей, если предоставлены неправильные аргументы, или HCD поврежден.

Возвращаемое значение: 0 при успеже, иначе отрицательный код ошибки.

void usb_hcd_resume_root_hub (struct usb_hcd *hcd)

Вызывается HCD для возобновления работы своего корневого хаба.

Параметры:

struct usb_hcd *hcd Хост-контроллер для этого корневого хаба.

Описание:

Хост-контроллер USB вызовет эту функцию, когде его корневой хаб приостановлен (с разрешенной фичей удаленного пробуждения, remote wakeup), и был получен запрос remote wakeup. Процедура отправляет запрос на постановку в очередь задач для возобновления работы корневого хаба (то есть для повторного управления его нисходящими портами).

int usb_bus_start_enum (struct usb_bus *bus, unsigned port_num)

Запустит немедленную энумерацию (для OTG).

Параметры:

struct usb_bus *bus Шина (должен использоваться фреймворк HCD)

unsigned port_num Номер порта по базе 1; обычно bus->otg_port.

Контекст: атомарный.

Описание:

Запустит энумерацию с немедленным сбросом, после чего hub_wq идентифицирует и возможно конфигурирует устройство USB. Это необходимо для драйверов контроллеров OTG, что помогает выполнить требования протокола HNP для синхронизации по времени к началу сброса порта.

Возвращаемое значение: 0 в случае успеха.

irqreturn_t usb_hcd_irq (int irq, void *__hcd)

Hook запросов прерываний (IRQ) для фреймворка HCD (связующее звено шины).

Параметры:

int irq Возникший IRQ.

void *__hcd Указатель на HCD, у которого произошел IRQ.

Описание:

Если контроллер не приостановлен (не в состоянии HALT), то вызовется обработчик прерывания драйвера. Проверяет, не остановлен ли контроллер.

Возвращаемое значение: IRQ_HANDLED, если IRQ был обработан, иначе IRQ_NONE.

void usb_hc_died (struct usb_hcd *hcd)

Сообщит о ненормальном выключении (abnormal shutdown) хост-контроллера (связующее звено шины).

Параметры:

struct usb_hcd *hcd Указатель на HCD, представляющий контроллер.

Описание:

Это вызывается связующим звеном шины, чтобы сообщить, что хост-контроллер USB остановлен, хотя все еще есть ожидающие обработки операции. Это вызывается автоматически связующим звеном PCI, так что это должны вызывать только связующие звенья шин, не относящихся к PCI.

Вызывайте эту функцию только с драйвером основного контроллера хоста (primary HCD).

struct usb_hcd *usb_create_shared_hcd (const struct hc_driver *driver,
                                       struct device *dev,
                                       const char *bus_name,
                                       struct usb_hcd *primary_hcd)

Создаст и инициализирует структуру HCD.

Параметры:

const struct hc_driver *driver Драйвер HC, который будет использовать этот HCD.

struct device *dev Устройство для этого HC, сохраненное в hcd->self.controller.

const char *bus_name Значение для сохранения в hcd->self.bus_name.

struct usb_hcd *primary_hcd Указатель на структуру usb_hcd, которая совместно использует устройство PCI. Выделяйте только определённые ресурсы для primary HCD.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Выделяет структуру usb_hcd с дополнительным пространством в конце для приватных данных драйвера HC. Инициализирует generic элементы структуры HCD.

Возвращаемое значение: в случае успеха указатель на выделенную и инициализированную структуру HCD. При неудаче (например если не хватает памяти) возвратит NULL.

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
                                struct device *dev,
                                const char *bus_name)

Создаст и инициализирует структуру HCD.

Параметры:

const struct hc_driver *driver Драйвер HC, который будет использовать этот HCD.

struct device *dev Устройство для этого HC, сохраненное в hcd->self.controller.

const char *bus_name Значение для сохранения в hcd->self.bus_name.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Выделяет структуру usb_hcd с дополнительным пространством в конце для приватных данных драйвера HC. Инициализирует generic элементы структуры HCD.

Возвращаемое значение: в случае успеха указатель на выделенную и инициализированную структуру HCD. При неудаче (например если не хватает памяти) возвратит NULL.

int usb_add_hcd (struct usb_hcd *hcd,
                 unsigned int irqnum,
                 unsigned long irqflags)

Завершает инициализацию и регистрацию generic структуры HCD.

Параметры:

struct usb_hcd *hcd Структура usb_hcd для инициализации.

unsigned int irqnum Линия прерывания для выделения.

unsigned long irqflags Флаги типа прерывания.

Описание:

Завершит остальные части инициализации generic HCD: выделит буферы целостной памяти, зарегистрирует шину, запрос линии IRQ, и вызовет подпрограммы reset() и start() драйвера.

void usb_remove_hcd (struct usb_hcd *hcd)

Обработка выключения (shutdown) для generic HCD.

Параметры:

struct usb_hcd *hcd Структура usb_hcd для удаления.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Отключит корневой хаб, затем отменяет эффекты usb_add_hcd(), вовлекая HCD-метод stop().

int usb_hcd_pci_probe (struct pci_dev *dev, const struct hc_driver *driver)

Инициализирует HCD, основанные на PCI.

Параметры:

struct pci_dev *dev Проверяемый USB Host Controller.

const struct hc_driver *driver Дескриптор драйвера USB HC.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Выделяет базовые ресурсы PCI для этого хост-контроллера USB, а затем вызывает метод start() для HCD, связанного с ним через запись driver_data горячего подключения.

Сохраните эту функцию в структуре pci_driver HCD как probe().

Возвращаемое значение: 0 в случае успеха.

void usb_hcd_pci_remove (struct pci_dev *dev)

Выключает обработку для РСВ, основанных на PCI.

Параметры:

struct pci_dev *dev Удаляемый USB Host Controller.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Отменяет эффект usb_hcd_pci_probe(), сначала вызвая HCD-метод stop(). Это всегда вызывается из контекста потока, нормально "rmmod", "apmd", или чего-нибудь подобного.

Сохраните эту функцию в структуре pci_driver HCD как remove().

void usb_hcd_pci_shutdown (struct pci_dev *dev)

Выключает хост-контроллер.

Параметры:

struct pci_dev *dev Выключаемый USB Host Controller.

int hcd_buffer_create (struct usb_hcd *hcd)

Инициализирует пулы буферов.

Параметры:

struct usb_hcd *hcd Шина, на которой инициализируются пулы буферов.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Вызовите это как часть инициализации хост-контроллера, который использует алокаторы памяти DMA. Это инициализирует некие пулы когерентной для DMA памяти, которые будут использоваться всем драйверами этого контроллера.

Вызовите hcd_buffer_destroy() для очистки после использования этих пулов.

Возвращаемое значение: 0 в случае успеха, иначе отрицательное значение errno.

void hcd_buffer_destroy(struct usb_hcd *hcd)

Отменяет выделение пулов буферов.

Параметры:

struct usb_hcd *hcd Шина, на которой уничтожаются пулы буферов.

Контекст: контекст задачи, возможен уход в сон.

Описание:

Это освободить пулы буферов, созданные вызовом hcd_buffer_create().

[Узлы символьных устройств USB]

В этой главе представлены узлы символьного устройства (Linux character device nodes). Возможно, вам лучше избежать написания нового кода ядра для вашего USB-драйвера. Драйверы устройств пользовательского режима обычно упакованы в приложения или библиотеки и могут использовать символьные устройства через библиотеку программирования, которая их оборачивает. К таким библиотекам относятся:

● libusb for C/C++, и
● jUSB for Java.

Некоторую старую информацию об этом можно увидеть в секции "USB Device Filesystem" руководства USB Guide. Последнюю копию USB Guide можно найти на страничке http://www.linux-usb.org/.

Замечание: раньше это было реализовано с помощью usbfs, но это не является частью отладочного интерфейса sysfs. Данная документация неполная, особенно в отношении асинхронного режима. Начиная с версии ядра 2.5.66, необходимо провести перекрёстный анализ кода и этой (новой) документации.

Какие файлы находятся в "devtmpfs"? Обычно монтируемая в /dev/bus/usb/, система usbfs включает в себя:

● /dev/bus/usb/BBB/DDD ... магические файлы, предоставляющие дескрипторы конфигурации каждого устройства и поддерживающие серию ioctl-вызовов для выполнения запросов к устройствам, включая операции ввода-вывода (исключительно для доступа программ).

Каждой шине присваивается номер (BBB), основанный на дате её энумерации; внутри каждой шины каждому устройству присваивается аналогичный номер (DDD). Эти пути BBB/DDD не являются "стабильными" идентификаторами; они могут измениться, даже если вы всегда оставляете устройства подключенными к одному и тому же порту концентратора. Даже не думайте сохранять их в файлах конфигурации приложений. Стабильные идентификаторы доступны для приложений пользовательского режима, которым они нужны. Устройства HID и сетевые устройства предоставляют эти стабильные идентификаторы, поэтому, например, вы можете быть уверены, что правильно отдали команду ИБП на отключение второго сервера. Обратите внимание, что он (пока) не раскрывает эти идентификаторы.

/dev/bus/usb/BBB/DDD

Используйте эти файлы одним из следующих основных способов:

● Их можно читать, получая сначала дескриптор устройства (18 байт), а затем дескрипторы текущей конфигурации. Подробнее об этих форматах двоичных данных см. в спецификации USB 2.0. Вам потребуется преобразовать большинство многобайтовых значений из формата little endian в ваш собственный порядок байтов хоста, хотя у некоторых полей в дескрипторе устройства (как поля в кодировке BCD, так и идентификаторы поставщика VID и продукта PID) байты будут переставлены автоматически. Обратите внимание, что дескрипторы конфигурации включают дескрипторы интерфейсов, altsettings, конечных точек и возможно дополнительных дескрипторов классов.

● Операции USB с помощью запросов ioctl() для выполнения запросов ввода-вывода конечных точек (синхронно или асинхронно) или управления устройством. Для этих запросов требуется функциональность CAP_SYS_RAWIO, а также разрешения на доступ к файловой системе. Одновременно можно выполнить только один запрос ioctl к одному из этих файлов устройств. Это означает, что если вы синхронно читаете данные с конечной точки из одного потока, вы не сможете записать данные на другую конечную точку из другого потока, пока чтение не завершится. Это работает для полудуплексных протоколов, но в противном случае вам пришлось бы использовать асинхронные запросы ввода-вывода.

Каждое подключенное USB-устройство имеет один файл. BBB указывает номер шины. DDD указывает адрес устройства на этой шине. Оба этих номера назначаются последовательно и могут использоваться повторно, поэтому на них нельзя полагаться для стабильного доступа к устройствам. Например, устройства довольно часто перенумеровываются, пока они подключены (например, кто-то задел блок питания, концентратор или USB-кабель), поэтому устройство может иметь номер 002/027 при первом подключении и 002/048 через некоторое время.

Эти файлы могут быть прочитаны как двоичные данные. Двоичные данные состоят из дескриптора устройства, а затем дескрипторов каждой конфигурации устройства. Многобайтовые поля в дескрипторе устройства преобразуются ядром в формат, соответствующий порядку байтов хоста. Дескрипторы конфигурации имеют формат, соответствующий порядку байтов шины! Дескриптор конфигурации отстоит друг от друга на wTotalLength байт. Если устройство возвращает меньше данных дескриптора конфигурации, чем указано в wTotalLength, в файле будет пробел для недостающих байтов. Эта информация также отображается в текстовом виде в файле /sys/kernel/debug/usb/devices, описанном далее.

Эти файлы также можно использовать для написания драйверов пользовательского уровня для USB-устройств. Вам нужно открыть файл /dev/bus/usb/BBB/DDD для чтения/записи, прочитать его дескрипторы, чтобы убедиться, что это именно то устройство, которое вы ожидаете, а затем подключиться к интерфейсу (или, возможно, нескольким) с помощью вызова ioctl. Вам нужно будет выполнить дополнительные вызовы ioctl к устройству для взаимодействия с ним с помощью управляющих, пакетных или других видов USB-передачи. Список IOCTL-файлов (IOCTLS) находится в файле linux/usbdevice_fs.h, и на момент написания статьи исходный код (linux/drivers/usb/core/devio.c) является основным источником информации о том, как получить доступ к устройствам через эти файлы.

Обратите внимание, что, поскольку по умолчанию эти файлы BBB/DDD доступны для записи только пользователю root, только пользователь root может записывать такие драйверы пользовательского режима. Вы можете выборочно предоставлять права на чтение/запись другим пользователям с помощью chmod. Также могут быть полезны параметры монтирования usbfs, такие как devmode=0666.

[Жизненный цикл драйверов User Mode]

Такому драйверу сначала необходимо найти файл устройства для устройства, с которым он умеет работать. Возможно, он узнал об этом, потому что агент обработки событий /sbin/hotplug выбрал этот драйвер для работы с новым устройством. Или возможно это приложение сканирует все файлы устройства /dev/bus/usb, и игнорирует большинство устройств. В любом случае он должен прочиатать через read() все дескрипторы из файла устройства и сверить их с теми, с которыми знает как работать. Он может просто отклонить все, кроме определенных VID и PID, или может потребоваться более сложная политика.

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

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

Скорее всего, вам понадобится драйвер более сложного типа: использующий не управляющие конечные точки, считывающий или записывающий данные и требующий исключительного использования интерфейса. Bulk транзакции данных проще всего использовать, но только родственные им interrupt транзакции работают с низкоскоростными устройствами. Как interrupt транзакции, так и изохронные транзакции обеспечивают гарантированное обслуживание, поскольку их полоса пропускания зарезервирована. Такие "периодические" транзакции передачи данных неудобно использовать через usbfs, если только вы не используете асинхронные вызовы. Однако interrupt транзакции также можно использовать в синхронном "one shot" стиле.

Ваш user-mode драйвер не должен беспокоиться об очистке состояния запроса, когда устройство отключено, хотя он должен закрыть открытый файл дескрипторов, как только начал видеть ошибки ENODEV.

[Запросы ioctl()]

Для использования этих ioctl вам нужно подключить следующие заголовки в вашу userspace программу:

#include < linux/usb.h>
#include < linux/usbdevice_fs.h>
#include < asm/byteorder.h>

Стандартные запросы модели USB-устройства из главы 9 спецификации USB 2.0 автоматически подключаются из заголовка < linux/usb/ch9.h>.

Если не указано нечто иное, описанные здесь запросы ioctl будут обновлять время модификации файла usbfs, к которому они применяются (за исключением случаев, когда они завершаются с ошибкой). Возвращаемый 0 означает успех; иначе возвращается стандартный код ошибки USB (эти коды документированы на страничке [5]).

Каждый из этих файлов мультиплексирует доступ к нескольким потокам ввода-вывода, по одному на каждую конечную точку. Каждое устройство имеет одну управляющую конечную точку (нулевая конечная точка), которая поддерживает ограниченный RPC-доступ в стиле RPC. Устройства настраиваются с помощью hub_wq (в ядре), устанавливая конфигурацию для всего устройства, которая влияет на такие параметры, как энергопотребление и базовый функционал. Конечные точки являются частью USB-интерфейсов, которые могут иметь альтернативные настройки, определяющие например доступные конечные точки. Многие устройства имеют только одну конфигурацию и интерфейс, поэтому драйверы для них будут игнорировать конфигурации и альтернативные настройки.

Запросы управления/состояния (Management/Status). Некоторые запросы USBFS не имеют прямого отношения к вводу/выводу устройств. Они в основном связаны с управлением устройствами и их состоянием. Все эти запросы синхронные.

USBDEVFS_CLAIMINTERFACE

Это используется для принудительного захвата usbfs определённого интерфейса, который ранее не был занят usbfs или каким-либо другим драйвером ядра. Параметр ioctl — это целое число, содержащее номер интерфейса (bInterfaceNumber из дескриптора).

Обратите внимание: если ваш драйвер не захватил интерфейс перед попыткой использовать одну из своих конечных точек, и ни один другой драйвер не привязан к нему, то интерфейс автоматически захватывается usbfs.

Этот захват освобождается с помощью RELEASEINTERFACE ioctl или путем закрытия файла дескриптора. Время модификации файла этим запросом не обновляется.

USBDEVFS_CONNECTINFO

Указывает, является ли устройство низкоскоростным. Параметр ioctl указывает примерно на такую ​​структуру:

struct usbdevfs_connectinfo {
unsigned int devnum;
unsigned char slow; };

Время модификации файла этим запросом не обновляется.

Вы не можете определить, подключено ли "не медленное" (not slow) устройство на high speed (480 Мбит/с) или только на full speed (12 Мбит/с). Значение devnum вам уже должно быть известно — это значение DDD имени файла устройства.

USBDEVFS_GET_SPEED

Возвратит скорость устройства. Скорость возвращается как числовое значение из перечисления usb_device_speed.

Время модификации файла этим запросом не обновляется.

USBDEVFS_GETDRIVER

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

struct usbdevfs_getdriver {
unsigned int interface;
char driver[USBDEVFS_MAXDRIVERNAME + 1]; };

Время модификации файла этим запросом не обновляется.

USBDEVFS_IOCTL

Передает запрос из пользовательского пространства через драйвер ядра, имеющий зарегистрированную им запись ioctl в структуре usb_driver:

struct usbdevfs_ioctl {
int ifno;
int ioctl_code;
void *data; };

/* user mode вызов выглядит примерно следующим образом.
* 'request' становится driver->ioctl() 'code' параметром.
* Размер 'param' кодируется в 'request', и данные копируются
* в или из driver->ioctl() 'buf' параметра.
*/
static int usbdev_ioctl (int fd, int ifno, unsigned request, void *param) {
struct usbdevfs_ioctl wrapper;

wrapper.ifno = ifno;
wrapper.ioctl_code = request;
wrapper.data = param;

return ioctl (fd, USBDEVFS_IOCTL, &wrapper); }

Время модификации файла этим запросом не обновляется.

Этот запрос позволяет драйверам ядра взаимодействовать с кодом пользовательского режима посредством операций с файловой системой, даже если они не создают символьное или блочное специальное устройство. Он также используется, например, для запроса устройству, какой специальный файл устройства следует использовать. Для отключения и повторного подключения драйверов ядра используются два предопределённых вызова ioctl, благодаря чему код пользовательского режима может полностью управлять привязкой и настройкой устройств.

USBDEVFS_RELEASEINTERFACE

Это используется для освобождения захвата usbfs, сделанного на интерфейсе, либдо неявно, либо из-за вызова USBDEVFS_CLAIMINTERFACE, перед закрытием файла дескриптора. Параметр ioctl это целое число, хранящее номер интерфейса (bInterfaceNumber из дескриптора); время модификации файла этим запросом не обновляется.

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

USBDEVFS_RESETEP

Сбрасывает значение data toggle для конечной точки (bulk или interrupt) в DATA0. Параметр ioctl это целое число для номера конечной точки (1 .. 15, как идентифицируется дескриптором конечной точки), с добавлением USB_DIR_IN, если конечная точка устройства посылает данные в хост.

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

USBDEVFS_DROP_PRIVILEGES

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

Поддержка синхронного I/O. Синхронгные запросы вовлекают блокировки ядра, пока не завершится запрос user mode, либо завершится успешно, либо с сообщением об ошибке. В большинстве случаев это самый простой способ использования usbfs, хотя, как отмечалось выше, он не позволяет выполнять ввод-вывод на более чем одной конечной точке одновременно.

USBDEVFS_BULK

Выдает запрос bulk read или write в устройство. Параметр ioctl это указатель на структуру:

struct usbdevfs_bulktransfer {
unsigned int ep;
unsigned int len;
unsigned int timeout; /* в миллисекундах */
void *data; };

Значение ep идентифицирует номер конечной точки bulk (1 .. 15, как идентифицировано дескриптором конечной точки), маскируемое USB_DIR_IN при обращении к конечной точке, которая отправляет данные с устройства на хост. Длина буфера данных определяется параметром len; последние версии ядер поддерживают запросы размером до 128 КБ. В FIXME описано, как возвращается длина чтения и как обрабатываются короткие чтения (short reads).

USBDEVFS_CLEAR_HALT

Очистит endpoint halt (stall) и сбросит endpoint toggle. Это имеет смысл только для конечных точек bulk или interrupt. Параметр ioctl это номер конечной точки (1 .. 15, как идентифицировано дескриптором конечной точки), маскируемое USB_DIR_IN при обращении к конечной точке, которая отправляет данные с устройства на хост.

Используйте это на приостановленных (stalled) конечных точках bulk или interrupt, с возвратом статуса -EPIPE в запрос транзакции данных. Не отправляйте запрос на управление напрямую, так как это может сделать запись хоста о переключении данных недействительной.

USBDEVFS_CONTROL

Отправляет управляющий запрос устройству. Параметр ioctl указывает на следующую структуру:

struct usbdevfs_ctrltransfer {
__u8 bRequestType;
__u8 bRequest;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
__u32 timeout; /* в миллисекундах */
void *data; };

Первые 8 байт этой структуры - содержимое пакета SETUP для отправки в устройство; для подробностей см. спецификацию USB 2.0. Значение bRequestType составлено комбинацией значений USB_TYPE_*, USB_DIR_* и USB_RECIP_* (из linux/usb.h). Если поле ненулевое, то оно описывает длину буфера данных data, содержиое которого либо записывается в устройство (USB_DIR_OUT), либо заполняется прочитанными данными из устройства (USB_DIR_IN).

На момент написания статьи вы не можете передавать на устройство или с него более 4 КБ данных; ограничение есть и у usbfs, и у некоторых драйверов хост-контроллера (обычно это не проблема). Кроме того, нельзя сказать, является недобустимым получение короткого обратного считывания (short read back) с устройства.

USBDEVFS_RESET

Выполняется сброс устройства уровня USB (USB level device reset). Параметр ioctl игнорируется. После сброса это приведет к повторной привязке (rebinds) всех интерфейсов устройства. Время модификации файла этим запросом не обновляется.

Предупреждение: избегайте использовать этот вызов, пока не будут испправлены некоторые баги usbcore, поскольку это не полностью синхронизирует состояние устройства, интерфейса и драйвера (не только usbfs).

USBDEVFS_SETINTERFACE

Установит альтернативную настройку для интерфейса. Параметр ioctl это указатель на примерно такую структуру:

struct usbdevfs_setinterface {
unsigned int interface;
unsigned int altsetting; };

Время модификации файла этим запросом не обновляется.

Эти элементы структуры относятся к некоторому дескриптору интерфейса, применяемому к текущей конфигурации. Номер интерфейса interface это значение bInterfaceNumber, а номер в altsetting это значение bAlternateSetting (это сбросит все конечные точки в интерфейсе).

USBDEVFS_SETCONFIGURATION

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

Предупреждение: избегайте использовать этот вызов, пока не будут испправлены некоторые баги usbcore, поскольку это не полностью синхронизирует состояние устройства, интерфейса и драйвера (не только usbfs).

Поддержка асинхронного I/O. Как упоминалось выше, существуют ситуации, когда может быть важно инициировать параллельные операции из кода пользовательского режима. Это особенно важно для периодических передач (interrupt и isochronous), но может использоваться и для других типов USB-запросов. В таких случаях асинхронные запросы, описанные здесь, незаменимы. Вместо того, чтобы отправлять один запрос и блокировать ядро до его завершения, блокировка осуществляется отдельно.

Эти запросы упаковываются в структуру, напоминающую URB, используемый драйверами устройств ядра )к сожалению, здесь нет поддержки асинхронного ввода-вывода POSIX). Он определяет тип конечной точки (USBDEVFS_URB_TYPE_*), конечную точку (номер, замаскированный USB_DIR_IN при необходимости), буфер и длину, а также значение пользовательского "контекста", служащее для уникальной идентификации каждого запроса (обычно это указатель на данные, относящиеся к запросу). Флаги могут изменять запросы (их количество не так велико, как поддерживается драйверами ядра).

В каждом запросе можно указать номер сигнала в реальном времени (от SIGRTMIN до SIGRTMAX включительно), чтобы запросить отправку сигнала после завершения запроса.

Когда usbfs возвратит эти URB-ы, значение status обновляется, и буфер buffer может быть модифицирован. За исключением isochronous транзакций, actual_length обновляется, чтобы сказать о количестве переданных байт; если установлен флаг USBDEVFS_URB_DISABLE_SPD ("short packets are not OK", короткие пакеты это нехорошо), то если было прочитано меньше байтов, чем было запрошено, вы получите отчет об ошибке:

struct usbdevfs_iso_packet_desc {
unsigned int length;
unsigned int actual_length;
unsigned int status; };

struct usbdevfs_urb {
unsigned char type;
unsigned char endpoint;
int status;
unsigned int flags;
void *buffer;
int buffer_length;
int actual_length;
int start_frame;
int number_of_packets;
int error_count;
unsigned int signr;
void *usercontext;
struct usbdevfs_iso_packet_desc iso_frame_desc[]; };

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

USBDEVFS_DISCARDURB

TBS Время модификации файла этим запросом не обновляется.

USBDEVFS_DISCSIGNAL

TBS Время модификации файла этим запросом не обновляется.

USBDEVFS_REAPURB

TBS Время модификации файла этим запросом не обновляется.

USBDEVFS_REAPURBNDELAY

TBS Время модификации файла этим запросом не обновляется.

USBDEVFS_SUBMITURB

TBS

TBS в контексте USBDEVFS_SUBMITURB означает To Be Sent (будет отправлен).

Давайте разберем подробнее.

Что такое USBDEVFS_SUBMITURB. Это команда (ioctl) в Linux для взаимодействия с подсистемой USB через специальный файловый устройства (/dev/bus/usb/...). Её основная задача — отправить URB (USB Request Block) драйверу USB хоста для последующей передачи на само USB-устройство.

URB — основная структура данных, используемая для любой связи по USB. Она содержит всю необходимую информацию для выполнения передачи:

● Адрес устройства и конечной точки (endpoint)
● Тип передачи (контрольная, прерывание, изохронная, массовая)
● Буфер данных (для отправки или приема)
● Функция обратного вызова (callback), которая будет выполнена по завершении передачи.

Где именно используется "TBS"? Строка TBS это не официальная часть API ядра, а внутренняя пометка (flag) в отладочных выводах ядра Linux (например, при использовании dmesg или cat /proc/kmsg).

Когда вы вызываете USBDEVFS_SUBMITURB, происходит следующее:

1. Ваше пользовательское приложение формирует структуру usbdevfs_urb и передает её ядру с помощью ioctl.
2. Ядро принимает этот URB, выполняет базовые проверки и помечает его внутренним состоянием, которое в отладочных сообщениях часто отображается как TBS.
3. Это состояние означает, что URB валиден, поставлен в очередь и ждет своей очереди на отправку по шине USB.
4. Планировщик USB хоста в нужный момент (в зависимости от типа передачи и загрузки шины) заберет этот URB из очереди и физически отправит его на устройство.
5. После завершения передачи (успешной или с ошибкой) состояние URB меняется, и вызывается ваша callback-функция, чтобы уведомить приложение о результате.

Пример в контексте. Представьте, что вы пишете драйвер для USB-модема. Вам нужно отправить команду AT для его настройки.

1. Вы создаете URB типа "interrupt" или "control транзакция", помещаете в него строку "AT+CFUN=1\r\n".
2. Вызываете ioctl(fd, USBDEVFS_SUBMITURB, &urb).
3. В этот момент в логах ядра (если включена отладка USB) может появиться запись, что URB для конечной точки 0x01 перешел в состояние TBS.
4. Через короткое время контроллер USB хоста физически отправит эти данные на модем.
5. Ядро получит ответное подтверждение (ACK) от модема, отметит URB как завершенный и вызовет вашу функцию, чтобы вы знали, что команда отправлена успешно.

Краткий итог:

USBDEVFS_SUBMITURB — команда "отправить этот URB на устройство".
URB — пакет с данными и инструкциями для USB-передачи.
TBS (To Be Sent) — внутреннее состояние ядра, означающее "URB принят, валиден и ждет в очереди на физическую отправку по шине USB".

Это низкоуровневая деталь, которая обычно не видна разработчикам приложений, но очень важна для разработчиков драйверов и отладки проблем с USB.

[Устройства USB]

Устройства USB теперь экспортируются через debugfs:

/sys/kernel/debug/usb/devices ... текстовый файл, показывающий каждый каждое из устройств USB, известных ядру, и их дескрипторы конфигурации. Вы также также использовать poll(), чтобы научиться этим новым устройствам.

/sys/kernel/debug/usb/devices

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

Этот файл в сочетании с системным вызовом poll() также может использоваться для определения добавления или удаления устройств:

int fd;
struct pollfd pfd;
fd = open("/sys/kernel/debug/usb/devices", O_RDONLY); pfd = { fd, POLLIN, 0 };
for (;;) {
/* При первом обращении этот вызов будет возвращен немедленно. */
poll(&pfd, 1, -1);

/* Чтобы увидеть, что изменилось, сравните предыдущее и текущее
содержимое файла или просканируйте файловую систему
(сканирование более точное). */ }

Обратите внимание, что это поведение предназначено для использования в информационных и отладочных целях. Более целесообразно использовать такие программы, как udev или HAL, например, для инициализации устройства или запуска вспомогательной программы пользовательского режима.

В этом файле выходные данные каждого устройства содержат несколько строк в формате ASCII.

Автор специально сделал их в формате ASCII, а не в двоичном формате, чтобы можно было получить полезные данные без использования вспомогательной программы. Однако с помощью вспомогательной программы числа в первых четырёх столбцах каждой строки T: (информация о топологии: Lev, Prnt, Port, Cnt) можно использовать для построения схемы топологии USB.

Каждая строка помечена односимвольным идентификатором:

T = Topology, топология (и другие топологиеские детали)
B = Bandwidth, полоса пропускания (применимо только для хост-контроллеров USB,
    которые виртуализированы как корневые хабы)
D = Device descriptor info, информация дескриптора устройства.
P = Product ID info, информация идентификатора продукта (из дескриптора
    устройства, но они не будут умещаться в одной строке)
S = строковые дескрипторы
C = информация дескриптора конфигурации (* = активная конфигурация)
I = информация дескприптора интерфайса
E = информация дескриптора конечной точки.

Выходной формат /sys/kernel/debug/usb/devices

Легенда:

d = десятичный номер (может иметь лидирующие нули)
x = шестнадцатеричный номер (может иметь лидирующие пробелы или нули)
s = строка

Topology info
T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd
|   |      |      |       |       |      |        |        |__MaxChildren
|   |      |      |       |       |      |        |__Device Speed in Mbps
|   |      |      |       |       |      |__DeviceNumber
|   |      |      |       |       |__Count of devices at this level
|   |      |      |       |__Connector/Port on Parent for this device
|   |      |      |__Parent DeviceNumber
|   |      |__Level in topology for this bus
|   |__Bus number
|__Topology info tag

Speed может быть:

1.5 Mbit/s для low speed USB
12 Mbit/s для full speed USB
480 Mbit/s для high speed USB (добавлено для USB 2.0)
5000 Mbit/s для SuperSpeed USB (добавлено для USB 3.0)

По причинам, затерявшимся в глубине веков, номер порта всегда меньше на 1. Например, устройство, подключенное к порту 4, будет отображаться с номером Port=03.

Bandwidth info
B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd
|   |                       |         |__Number of isochronous requests
|   |                       |__Number of interrupt requests
|   |__Total Bandwidth allocated to this bus
|__Bandwidth info tag

Распределение пропускной способности это приблизительное значение используемой части одного кадра (миллисекунды). Оно отражает только периодические передачи, которые являются единственными передачами, резервирующими пропускную способность. Control и bulk передачи используют всю остальную пропускную способность, включая зарезервированную полосу пропускания, не используемую для передач (например, для коротких пакетов, short packets).

Процентное значение показывает, какая часть "зарезервированной" пропускной способности выделяется для этих передач. Для low speed или full speed шины (условно, "USB 1.1") резервируется 90% пропускной способности шины. Для high speed шины (условно, "USB 2.0") резервируется 80%.

Device descriptor info & Product ID info
D:  Ver=x.xx Cls=xx(s) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx

Где:

D:  Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd
|   |        |             |      |       |       |__NumberConfigurations
|   |        |             |      |       |__MaxPacketSize of Default Endpoint
|   |        |             |      |__DeviceProtocol
|   |        |             |__DeviceSubClass
|   |        |__DeviceClass
|   |__Device USB version
|__Device info tag #1

Где:

P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx
|   |           |           |__Product revision number
|   |           |__Product ID code
|   |__Vendor ID code
|__Device info tag #2

String descriptor info
S:  Manufacturer=ssss
|   |__Manufacturer этого устройства, как прочитано из устройства.
|      Для драйверов хост-контроллера USB (virtual root-хабы) это может
|      быть опущено, или (для более новых драйверов) будет идентифицировать
|      версию ядра и жрайвер, который предоставил эту эмуляцию хаба.
|__String info tag

S:  Product=ssss
|   |__Product описание этого устройства, как прочитано из устройства.
|      Для старых драйверов хост-контроллеров USB (virtual root-хабов) это
|      показывает драйвер; для более новых это описание продукта (и вендора),
|      которое часто поступает из базы данных kernels PCI ID.
|__String info tag

S:  SerialNumber=ssss
|   |__Serial Number этого устройства, как прочитано из устройства.
|      Для драйверов хост-контроллера USB (virtual root-хабов) это некоторый
|      уникальный ID, обычно bus ID (адрес или имя слота), который не может
|      использоваться любым другим устройством.
|__String info tag

Configuration descriptor info
C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA
| | |       |       |      |__MaxPower in mA
| | |       |       |__Attributes
| | |       |__ConfiguratioNumber
| | |__NumberOfInterfaces
| |__ "*" показывает активную конфигурацию (другие " ")
|__Config info tag

USB-устройства могут иметь несколько конфигураций, каждая из которых действует по-своему. Например, конфигурация с питанием от шины может быть гораздо менее производительной, чем конфигурация с автономным питанием. Одновременно может быть активна только одна конфигурация устройства; большинство устройств имеют только одну конфигурацию.

Каждая конфигурация состоит из одного или нескольких интерфейсов. Каждый интерфейс выполняет определённую "функцию", которая обычно привязана к отдельному драйверу USB-устройства. Одним из распространённых примеров является USB-динамик с аудиоинтерфейсом для воспроизведения и HID-интерфейсом для использования с программным управлением громкостью.

Interface descriptor info (can be multiple per Config)
I:* If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss
| | |      |      |       |             |      |       |__Driver name
| | |      |      |       |             |      |          или "(none)"
| | |      |      |       |             |      |__InterfaceProtocol
| | |      |      |       |             |__InterfaceSubClass
| | |      |      |       |__InterfaceClass
| | |      |      |__NumberOfEndpoints
| | |      |__AlternateSettingNumber
| | |__InterfaceNumber
| |__ "*" показывает активную altsetting (другие " ")
|__Interface info tag

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

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

Endpoint descriptor info (can be multiple per Interface)
E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddss
|   |        |            |         |__Interval (max) between transfers
|   |        |            |__EndpointMaxPacketSize
|   |        |__Attributes(EndpointType)
|   |__EndpointAddress(I=In,O=Out)
|__Endpoint info tag

Interval для всех периодических (interrupt или isochronous) конечных точек. Для high speed конечных точек интервал транзакции может измеряться в микросекундах вместо миллисекунд.

Для high speed периодических конечных точек EndpointMaxPacketSize отражает размер данных транзакции на микрофрейм. Для конечных точек "high bandwidth" это может отражать 2 или 3 пакета (до 3 килобайт каждые 125 мкс) на конечную точку.

Со стеком Linux-USB периодические резервирования полсы пропускания используют интервалы транзакций и размеры, предоставленные URB-ами, которые могут быть меньше, чем найдены в дескрипторе конечной точки.

Примеры применения. Если пользователя или скрипт интересует только информация Topology, то используйте что-то типа grep ^T: /sys/kernel/debug/usb/devices только для отображения строк топологии. Команда наподобие grep -i ^[tdp]: /sys/kernel/debug/usb/devices может использоваться для листинга только строк, которые начинаются с символами в квадратных скобках, где допустимыми символами являются TDPCIE. С помощью немного более сложного скрипта он может отображать любые выбранные строки (например, только строки T, D и P) и изменять формат их вывода (скрипт Perl procusb — начало этой идеи. Он выведет только выбранные строки [выбранные из TBDPSCIE] или "All" строки из /sys/kernel/debug/usb/devices).

Строки топологии можно использовать для создания графического/иллюстрационного представления USB-устройств на корневом хабе системы (подробнее о том, как это сделать, см. ниже).

Строки Interface можно использовать для определения того, какой драйвер используется для каждого устройства и какие настройки altsetting он активирует.

Строки Configuration можно использовать для вывода максимальной мощности (в миллиамперах), потребляемой USB-устройствами системы. Например, grep ^C: /sys/kernel/debug/usb/devices.

Вот пример из системы, в которой есть корневой концентратор UHCI, внешний концентратор, подключенный к корневому концентратору, а также мышь и serial-конвертер, подключенные к внешнему хабу.

T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12   MxCh= 2
B:  Alloc= 28/900 us ( 3%), #Int=  2, #Iso=  0
D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0000 ProdID=0000 Rev= 0.00
S:  Product=USB UHCI Root Hub
S:  SerialNumber=dce0
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms

T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12   MxCh= 4
D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0451 ProdID=1446 Rev= 1.00
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=255ms 

T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5  MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=04b4 ProdID=0001 Rev= 0.00
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
E:  Ad=81(I) Atr=03(Int.) MxPS=   3 Ivl= 10ms

T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0565 ProdID=0001 Rev= 1.08
S:  Manufacturer=Peracom Networks, Inc.
S:  Product=Peracom USB to Serial Converter
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial
E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl= 16ms
E:  Ad=01(O) Atr=02(Bulk) MxPS=  16 Ivl= 16ms
E:  Ad=82(I) Atr=03(Int.) MxPS=   8 Ivl=  8ms

Выбрав из этого только строки T: и I: (например, с помощью procusb ti), мы имеем:

T:  Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12   MxCh= 2
T:  Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12   MxCh= 4
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
T:  Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=1.5  MxCh= 0
I:  If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=mouse
T:  Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial

Физически это выглядит примерно так (или может быть преобразовано в подобный вид):

                    +------------------+
                    |  PC/root_hub (12)|   Dev# = 1
                    +------------------+   (nn) is Mbps.
Level 0             |  CN.0   |  CN.1  |   [CN = connector/port #]
                    +------------------+
                        /
                       /
          +-----------------------+
Level 1   | Dev#2: 4-port hub (12)|
          +-----------------------+
          |CN.0 |CN.1 |CN.2 |CN.3 |
          +-----------------------+
              \           \____________________
               \_____                          \
                     \                          \
             +--------------------+      +--------------------+
Level 2      | Dev# 3: mouse (1.5)|      | Dev# 4: serial (12)|
             +--------------------+      +--------------------+

Или в более древовидной структуре (порты [Connectors] без соединений можно опустить):

PC:  Dev# 1, root hub, 2 ports, 12 Mbps
|_ CN.0:  Dev# 2, hub, 4 ports, 12 Mbps
     |_ CN.0:  Dev #3, mouse, 1.5 Mbps
     |_ CN.1:
     |_ CN.2:  Dev #4, serial, 12 Mbps
     |_ CN.3:
|_ CN.1:

[Ссылки]

1. The Linux-USB Host Side API.
2. USB in a NutShell - путеводитель по стандарту USB (начало).
3. Q006. Что такое buildroot?
4. Порядок следования байт (endianness).
5. USB Error codes.