Usb host controller что это

Обновлено: 06.07.2024

USB OTG (аббр. от On-The-Go) — дальнейшее расширение спецификации USB 2.0, предназначенное для лёгкого соединения периферийных USB-устройств друг с другом без необходимости подключения к ПК. Например, цифровой фотоаппарат можно подключать к фотопринтеру напрямую, если они оба поддерживают стандарт USB OTG. К моделям КПК и коммуникаторов, поддерживающих USB OTG, можно подключать некоторые USB-устройства. Обычно это флэш-накопители, цифровые фотоаппараты, клавиатуры, мыши и другие устройства, не требующие дополнительных драйверов. Этот стандарт возник из-за резко возросшей в последнее время необходимости надёжного соединения различных устройств без использования ПК.
При подключении через USB OTG ранг устройства (ведущий или ведомый) определяется наличием перемычки к ID штекеру в проводе.

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

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

К одному хосту по шине USB можно подключить несколько устройств, включая концентраторы (USB-хабы) . В настоящее время наиболее распространены устройства стандарта USB 2.0, а недавно также появилась спецификация 3.0, обеспечивающая еще более быструю передачу данных.

Наличие USB-хоста на каком-нибудь устройстве означает возможность подключения к нему различных внешних устройств — например, флэшек, внешних жестких дисков, кардридеров, плееров, фотоаппаратов и так далее. Есть возможность подключения и внешней «периферии» — клавиатур, мышек и так далее. Проще говоря это стандартный разъём USB.

USB OTG не предполагает наличие отдельного классического полноразмерного порта USB. Для подключения периферийных устройств используется порт microUSB или miniUSB, который, вообще говоря, служит главным образом для связи устройства с компьютером. Чтобы к miniUSB/microUSB порту с поддержкой OTG подключить флэшку или, клавиатуру, необходимо приобрести специальный переходник, который стоит порядка 500 рублей (при желании его можно найти по более низкой цене или же вовсе сделать самому — в интернете есть инструкции) . Затем нужно вставить этот переходник в порт miniUSB/microUSB, а к соответствующему выходу переходника подключить нужное вам периферийное устройство.



Прежде, чем объяснять код поддержки хост-контроллеров, необходимо рассказать о некоторых принципах работы железа, а также об используемых структурах данных. Как я выяснила при написании текста, одна статья обо всём уровне поддержки хост-контроллеров получилась бы слишком большой, поэтому вторая часть цикла — которую вы сейчас читаете — рассказывает о том, что необходимо знать для понимания кода, а описание действий, происходящие в коде, я отложу до следующей части.

Прерывания и потоки

Хост-контроллеры оповещают софт о происходящих событиях, генерируя прерывания. Прерывание может прийти и оторвать процессор от текущей задачи в любой момент времени; это накладывает жёсткие требования на обработчик прерывания. Обработчик прерывания не может захватывать никакие блокировки — ведь вполне возможно, что прерванный код как раз завладел блокировкой и уже не сможет её освободить. Единственным исключением является вариант спинлока, запрещающий прерывания на время блокировки, но из-за глобальности эффекта спинлок стоит применять пореже и для очень коротких участков кода. На однопроцессорных конфигурациях такой вариант вырождается в пару cli / sti без собственно спинлока, на многопроцессорных внутри cli / sti остаётся обычный спинлок. Кроме того, контроллер прерываний во время обработки одного прерывания блокирует остальные с тем же или более низким приоритетом.

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

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

Зависящие и не зависящие от контроллера компоненты структур


Для уровня поддержки хост-контроллеров важны следующие структуры: структура данных контроллера *_controller , структура данных канала *_pipe , структура данных неизохронной передачи *_gtd . Каждая из них состоит из двух частей: специфичной для хост-контроллера *hci_* и общей для всех контроллеров usb_* . Хост-контроллер требует выравнивания своих структур. Данные контроллера используют выравнивание на границу страницы, то есть 1000h байт. Выравнивание прочих данных разное для разных контроллеров.

В KolibriOS обе части каждой структуры располагаются в памяти последовательно. Память под обе структуры выделяется одним приёмом с учётом требуемого выравнивания. Первой в памяти идёт часть, отвечающая за общение с хост-контроллером, чтобы обеспечить выравнивание. Для адресации обеих частей используется один указатель, указывающий на границу между частями; по отрицательным смещениям находятся данные *hci_* , по неотрицательным — данные usb_* . Указатель на usb_controller постоянно размещается в регистре esi . Хэндл канала представляет собой указатель на usb_pipe ; одним из полей usb_pipe является указатель на соответствующий usb_controller .

Код, выделяющий память под структуры, должен знать размеры обеих структур и требуемое выравнивание. Для *_controller используется постраничный аллокатор, автоматически гарантирующий выравнивание на границу страницы. Аллокатор вызывается кодом, ответственным за usb_controller , размер структуры *hci_controller берётся из usb_hardware_func.DataSize ; как я упоминала в общем обзоре, usb_hardware_func описывает вещи, специфичные для хост-контроллера, остальному коду.
Для *_pipe и *_gtd выделять по странице на каждый экземпляр было бы крайне расточительно, а использовать общую кучу ядра для малых блоков — неудобно из-за требований выравнивания. Поэтому для них код использует аллокатор блоков фиксированного размера, который, выделив страницу, нарезает её на блоки заданного размера и отдаёт их один за другим. Если выделяемый размер кратен, например, 16 байтам, то все выделяемые блоки будут иметь адрес, кратный 16. Здесь аллокатору для каждого размера нужны отдельные данные; чтобы не включать их все в структуру usb_hardware_func , последняя содержит функции выделения/освобождения AllocPipe / FreePipe для пары структур *_pipe и AllocTD / FreeTD для пары структур *_gtd .

Хост-контроллер должен знать физические адреса всех структур, чтобы работать с ними. Адрес структуры *hci_controller заносится в ходе инициализации контроллера. Адреса структур данных неизохронных передач собраны в односвязный список с физическим адресом первого элемента внутри *hci_pipe и физическим адресом каждого следующего элемента внутри *hci_gtd .




Каналы сгруппированы в несколько списков. Внутри каждого списка есть три связи: физический адрес следующего канала для железа, виртуальные адреса следующего и предыдущего каналов для софта. Один список состоит из всех каналов для управляющих передач. Другой список состоит из всех каналов для передач массивов данных.
Списки каналов прерываний организованы в двоичное дерево так, как показано на рисунке, где кружки обозначают списки каналов прерываний, а стрелки — физические адреса следующих элементов. Хост-контроллер начинает каждую единицу времени (фрейм для UHCI и OHCI, микрофрейм для EHCI) с того, что берёт младшие n бит номера фрейма (именно фрейма, даже если это EHCI), берёт соответствующий элемент таблицы адресов, являющейся частью *hci_controller , и начинает идти по ссылкам на следующий элемент. Первый список, таким образом, будет обрабатываться один раз каждые 2 n миллисекунд. Дальше пары ссылок «склеиваются»: на следующий список ведёт две ссылки так, чтобы следующий список получал внимание контроллера дважды за полный цикл по таблице адресов, один раз каждые 2 n-1 миллисекунд. В конце располагается список, элементы которого обрабатываются каждую миллисекунду. Такая организация каналов прерываний позволяет реализовать каналы с интервалом обработки, выражающимся в миллисекундах степенью двойки. Спецификация USB разрешает делать реальный интервал опроса меньше запрошенного.

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

Поддержка изохронных передач находится на стадии разработки, поэтому пока я скажу только несколько слов про аппаратную часть. В OHCI изохронные передачи адресуются аналогично остальным: в ohci_pipe есть бит, отвечающий за формат структур данных передачи, изохронные и остальные используют разный формат. В UHCI и EHCI структуры данных для изохронных каналов как таковой нет, а структуры изохронных передач вставляются в таблицу адресов наравне со структурами каналов прерываний. Чтобы контроллер мог понять, указывает ли адрес на канал или на изохронную передачу (которых на самом деле есть два разных типа), два бита адреса отводятся под тип структуры, которая по этому адресу находится. Как следствие, число n для UHCI и EHCI равно 10, но не для поддержки интервалов опроса в секунду с лишним, а для того, чтобы после обработки фрагмента изохронной передачи у софта была секунда на запрос следующего фрагмента. В OHCI n=5.

Передачи и транзакции

Хотя протоколы архитектуры USB ниже передач почти неинтересны, но есть некоторые вещи, которые о них знать всё же необходимо при реализации уровней ниже уровня драйверов.
Размер передачи по шине USB практически неограничен; чтобы одно устройство не занимало шину слишком надолго, передачи разбиваются на транзакции. За одну транзакцию передаётся очередной фрагмент данных ограниченной длины. Максимальная длина транзакции — одна из характеристик канала. Для одного этапа передачи (я напомню, что управляющие передачи состоят из двух или трёх этапов, а остальные — из одного этапа) все транзакции, кроме последней, имеют максимальный размер; последняя транзакция передаёт оставшиеся данные и может быть короче остальных.

Размер данных, которые может описать одна пара структур *_gtd , также ограничен. Если все данные не умещаются в одну *_gtd , передачу нужно разбивать на несколько частей. Места разбиения нужно выбирать так, чтобы с точки зрения устройства происходящее оставалось одной передачей, то есть размер всех частей, кроме последней, должен делиться на максимальный размер транзакции.

UHCI — хронологически первый интерфейс, созданный Intel; в UHCI упор делается на простоту аппаратной реализации. Как следствие, UHCI-контроллер ничего не знает про передачи, а одна структура uhci_gtd описывает одну транзакцию. Для больших передач это приводит к большим накладным расходам на отдельную память для всех транзакций.
В OHCI и EHCI контроллер уже умеет самостоятельно разбивать длинные передачи на транзакции, здесь ограничения слабее. В ohci_gtd есть два поля для двух страниц данных, в лучшем случае получается 2000h байт, в худшем (если данные начинаются с адреса xxxxxFFFh ) — 1001h байт = 4 килобайта + 1 байт. В ehci_gtd помещаются уже пять страниц, что в худшем случае даёт ограничение 4001h байт. Если данных больше, то передачу по-прежнему нужно разбивать на несколько фрагментов.

В USB2 появились расщеплённые транзакции (split transactions). Спецификация USB2 добавила новую скорость передачи данных 480 мегабит/с (high-speed, HS), но по-прежнему поддерживает две скорости USB1, 12 мегабит/с (full-speed, FS) и 1.5 мегабит/с (low-speed, LS). На одной шине USB в каждый момент времени можно общаться только с одним устройством. В USB1 шина, управляемая одним хост-контроллером, была единой, и во время транзакции к LS-устройству она (способная на 12 мегабит/с) работала со скоростью 1.5 мегабит/с. В USB2 аналогичным образом замедлять HS-шину было бы непрактично, поэтому выделяется одна общая шина, которая всегда работает на high-speed, и несколько FS/LS-шин, к которым подключаются FS/LS-устройства. За связь между шинами отвечает хаб, к которому подключено низкоскоростное устройство; спецификация называет соответствующую часть хаба Transaction Translator (TT).

Пока хаб медленно общается с низкоскоростным устройством по низкоскоростной шине, высокоскоростная шина оказывается свободной, причём довольно надолго. Чтобы полученное время можно было использовать с толком, транзакция по HS-шине расщепляется на две: начальную (start-split transaction) и конечную (complete-split transaction).




Детали расщепления несколько различаются для периодических транзакций (передач по прерыванию и изохронных передач) и непериодических (управляющих передач и передач массивов данных). На рисунке выше показана схема происходящего внутри хаба для периодических расщеплённых транзакций. Хорошая новость: для непериодических транзакций дополнительные действия по поддержке минимальны — нужно правильно инициализировать структуру канала и при ошибке HS-шины очищать буфер хаба с данными, за остальным будет следить сам контроллер. Для периодических транзакций всё сложнее. Именно отсюда возникает вторая битовая маска в структуре канала прерываний, которую я ранее упоминала, — для каналов прерываний FS/LS-устройств первая битовая маска отвечает за микрофреймы, в которые нужно инициировать начальную расщеплённую транзакцию, вторая — за микрофреймы, в которые нужно инициировать конечную расщеплённую транзакцию. Отсюда же появляется второй тип изохронных транзакций в EHCI — структуры обычной и расщеплённой изохронных транзакций различаются.

EHCI и компаньоны



При проектировании хост-контроллера для USB2 Intel решила по возможности задействовать уже существующую базу в виде железа UHCI/OHCI и программной поддержки. В корневом хабе EHCI отсутствует Transaction Translator; вместо него каждый порт может быть подключён к контроллеру-компаньону, им может быть UHCI или OHCI. Компаньонов может быть несколько. Пока EHCI-контроллер не инициализирован, все порты подключены к компаньонам; код, умеющий программировать UHCI и OHCI, сможет работать со всеми устройствами и в такой конфигурации, естественно, на скорости USB1. После инициализации EHCI-контроллера каждому порту можно назначить владельца независимо от других. Контроллер, не являющийся владельцем, воспринимает порт в состоянии «нет устройства». Порты, на которых действительно нет устройства, а также порты с HS-устройствами назначаются контроллеру EHCI; порты с низкоскоростными устройствами назначаются контроллеру-компаньону.




Позднее Intel решила, что больше не хочет ставить UHCI рядом с EHCI. Чтобы не переделывать спецификацию и не заставлять всех переписывать драйверы, Intel не стала менять контроллер, но на пути от «настоящих» портов до контроллера поставила «виртуальный» хаб с официальным названием Rate Matching Hub (RMH), а контроллеру оставила только два порта, к одному из которых всегда подключён хаб. Назначение второго порта, к сожалению, мне выяснить не удалось. С программной точки зрения «виртуальный» хаб ничем не отличается от обычного, просто при написании своей реализации следует иметь в виду, что для доступа к устройствам на некоторых конфигурациях придётся реализовать не только поддержку EHCI, но и поддержку хабов.

image

Всех приветствую. Сегодня хочу поделиться опытом и всё-таки по-моему внятно объяснить про такой, на первый взгляд, простой стандарт для USB 2.0 хост-контроллера.

Изначально можно представить себе что USB 2.0 порт — это всего лишь 4 пина, по двум из которых просто передаются данные(Как, к примеру, COM-порт), но самом деле всё не так, и даже совсем наоборот. USB-контроллер в принципе не даёт нам возможности передавать данные как через обычный COM-порт. EHCI — довольно замысловатый стандарт, который позволяет обеспечить надежную и быструю передачу данных от софта до самого девайса, и в обратную сторону.

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

Что такое EHCI

Что же, давайте начнем. EHCI — Enhanced Host Controller Interface, предназначен для передачи данных и управляющих запросов USB-устройствам, и в другую сторону, а в 99% случаев — является связующим звеном, между каким-либо софтом и физическим устройством. EHCI работает как PCI-устройство, а соответственно использует MMIO(Memory-Mapped-IO) для управления контроллером(да-да, я знаю, что некоторые PCI-девайсы используют порты, но тут я всё обобщил). В документации от Intel описан лишь принцип работы, и никаких намеков на алгоритмы, написанные хотя бы на псевдокоде, нет вовсе. EHCI имеет 2 типа MMIO-регистров: Capability и Operational. Первые служат для получения характеристик контроллера, вторые же — для его управления. Собственно, прикреплю саму суть связи софта и EHCI контроллера:

image

Каждый EHCI контроллер имеет несколько портов, каждому из которых могут быть подключены какие-либо USB-устройства. Так же, прошу заметить, что EHCI является улучшенной версией UHCI, который так же был разработан Intel на несколько годов раньше. Для обратной совместимости любой UHCI/OHCI контроллер, который имеет версию ниже, чем EHCI, будет компаньоном к EHCI. К примеру, у вас есть USB-клавиатура(А большинство клавиатур года так до сих пор были именно такими), которая работает на USB 1.1(заметим, что максимальная скорость работы USB 1.1 — 12 мегабит в секунду, а FullSpeed USB 2.0 имеет пропускную способность аж в 480 мбит/сек), а у Вас имеется компьютер с USB 2.0 портом, при подключении клавиатуры к компьютеру хост-контроллер EHCI как ни как будет работать с USB 1.1. Данная модель показана на следующей схеме:

image

Так же на будущее хочу сразу предупредить, что Ваш драйвер может работать не правильно из-за такой вот нелепой ситуации: вы инициализировали UHCI, а после чего EHCI, при этом добавили два одинаковых устройства, поставили в регистр порта бит Port Owner Control, после чего UHCI перестал работать, из-за того, что EHCI автоматически перетягивает порт на себя, а порт на UHCI перестаёт откликаться, эту ситуацию надо отслеживать.

Так же, давайте рассмотрим схему, показывающую саму архитектуру EHCI:

image

Справа написано про очереди — о них чуть позже.

Регистры EHCI контроллера

Для начала хочется еще раз уточнить, что через данные регистры вы будете управлять вашим устройством, поэтому они очень важны — да и без них программирование EHCI невозможно.

Для начала вам надо получить адрес MMIO, который выдан данному контроллеру, по смещению +0x10 будет лежать адрес наших долгожданных регистров. Есть одно но: сначала идут Capability регистры, а только после них — Operational, поэтому по смещению 0(от предыдущего адреса, который мы получили по смещению 0x10 относительно начала MMIO нашего EHCI) лежит один байт — длина Capability-регистров.

Capability регистры

По смещению 2 лежит регистр HCIVERSION — номер ревизии данного HC, который занимает 2 байта и содержит BCD версию ревизии (что такое BCD можно узнать из википедии).
По смещению +4 лежит регистр HCSPARAMS, его размер — 2 слова, он содержит структурные параметры устройства и его биты показывают следующее:

  • Бит 16 — Port Indicators — доступные световые индикаторы для подключенных USB-устройств.
  • Биты 15:12 — номер контроллера-компаньона, который присвоен данному контроллеру
  • Биты 11:8 — количество портов у компаньон-контроллера
  • Бит 7 — Port Routing Rules — показывает, как данные порты привязаны к компаньон-портам
  • Бит 4 — Port Power Control — показывает, надо ли включать питание каждому порту, 0 — питание подаётся автоматически
  • Биты 3:0 — количество портов у данного контроллера.
  • По смещению +8 лежит регистр HCCPARAMS — показывает параметры совместимости, его биты значат следующее:
  • Бит 2 — доступность асинхронной очереди,
  • Бит 1 — доступность периодической (последовательной) очереди
  • Бит 0 — 64-битная совместимость

Operation регистры

По смещению 0 лежит регистр USBCMD — командный регистр контроллера, его биты означают следующее:

  • Биты 23:16 — Interrupt Threshold Control — показывает сколько микро-фреймов будет использоваться на один обычный фрейм. Чем больше, тем быстрее, но если больше 8 — то микро-фреймы будут обрабатываться с той же скоростью, что и для 8.
  • Бит 6 — прерывание после каждой транзакции в асинхронной очереди,
  • Бит 5 — используется ли асинхронная очередь,
  • Бит 4 — использование последовательной очереди,
  • Биты 3:2 — размер FrameList'a (о этом — дальше). 0 означает 1024 элемента, 1 — 512, 2 — 256, 3 — зарезервировано
  • Бит 1 — устанавливается для выполнение сброса хост-контроллера.
  • Бит 0 — Run/Stop
  • Бит 15 показывает используется ли асинхронная очередь
  • Бит 14 показывает используется ли последовательная очередь,
  • Бит 13 — показывает, что обнаружена пустая асинхронная очередь,
  • Бит 12 установлен в 1, если при обработке транзакции произошла ошибка, тогда хост-контроллер остановит выполнение всех очередей.
  • Бит 4 установлен в 1, если произошла серьезная ошибка, хост-контроллер останавливает выполнение всех очередей.
  • Бит 3 FrameList (Регистр) Rollover — ставится в 1, когда хост-контроллер обработал весь frameList.
  • Бит 1 — USB Error Interrupt — генерировать ли прерывание при ошибках?
  • Бит 0 — USB Interrupt — выставляется после успешной обработки транзакции, если в TD был установлен IOC

По смещению +8 лежит регистр USBINTR — регистр включения прерываний
Чтобы долго не писать, и тем более, Вам долго не читать, значения битов данного регистра можно посмотреть в спецификации, ссылка на неё будет оставлена внизу. Сюда я просто записываю 0, т.к. абсолютно не имею желания писать обработчики, мапить прерывания и т.п., так что это я считаю почти что абсолютно бессмысленным.

По смещению +12(0x0C) лежит регистр FRINDEX, в котором просто лежит текущий номер фрейма, при чем, хочу заметить, что последние 4 бита показывают номер микро-фрейма, в старшие 28 — номер фрейма (так же значение не обязательно меньше размера frameList'а, если вам нужен индекс — лучше брать его с маской 0x3FF(или же 0x1FF, и т.п.).

Регистр CTRLDSSEGMENT лежит по смещению +0x10, он показывает хост-контроллеру старшие 32 бита адреса листа фреймов.

Регистр PERIODICLISTBASE имеет смещение +0x14, в него вы можете положить младшие 32 бита листа фреймов, заметим, что адрес должен быть выравнен по размеру страницы памяти (4096).

Регистр ASYNCLISTADDR имеет смещение +0x18, в него вы можете положить адрес асинхронной очереди, заметим, что он должен быть выравнен по границе 32 байта, при этом должен находиться в первых четырех гигабайтах физической памяти.

Регистр CONFIGFLAG показывает, настроено ли устройство. Вы должны выставить бит 0 после завершения настройки устройства, он имеет смещение +0x40.

Перейдем к регистрам портов. Каждый порт имеет свой командно-статусный регистр, каждый регистр порта располагается со смещением +0x44 + (PortNumber — 1)*4, его биты значат следующее:

  • Бит 12 — питание порта, 1 — питание подаётся, 0 — нет.
  • Бит 8 — Port Rest — устанавливается для сброса устройства.
  • Бит 3 — Port Enable/Disable Change — выставляется при изменении статуса «включенности» порта.
  • Бит 2 — порт включен/не включен.
  • Бит 1 — Изменение статуса подключения, ставится в 1, к примеру, если вы подключили, или отключили USB устройство.
  • Бит 0 — статус подключения, 1 — подключено, 0 — нет.

Структуры передачи данных и запросов

Организация структуры для обработки запросов включает в себя очередь и трансфер дескрипторы(TDs).

На данный момент мы рассмотрим только 3 структуры.

Последовательный список

Последовательный(Периодичный, Pereodic) список устроен следующим образом:

image

Как видно на схеме, обработка начинается с получения нужного фрейма из фрейм листа, каждый его элемент занимает 4 байта и имеет следующую структуру:

image

Как видно на картинке, адрес очереди/трансфер дескриптора выровнен по границе 32 байта, бит 0 означает то, что хост-контроллер не будет обрабатывать данный элемент, биты 3:1 показывают тип того, что будет обрабатывать хост-контроллер: 0 — изосинхронный TD(iTD), 1 — очередь, 2 и 3 в данной статье я рассматривать не буду.

Асинхронная очередь

Хост контроллер обрабатывает данную очередь только тогда, когда фрейм последовательный пустой, либо хост-контроллер обработал весь последовательный список.

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

image

qTD(Queue Element Transfer Descriptor)

Данный TD имеет следующую структуру:

image

Next qTD Pointer — указатель на продолжение очереди для обработки(для Horizontal Execution), бит 0 Next qTD Pointer'а показывает, то, что дальше нет еще одной очереди.
qTD Token — токен TD, показывает параметры передачи данных:

  • Бит 31 — Data Toggle (об этом дальше)
  • Биты 30:16 — количество данных для передачи, после завершения транзакции их значение уменьшается на количество переданных данных.
  • Бит 15 — IOC — Interrupt On Complete — вызвать прерывание после завершения обработки дескриптора.
  • Биты 14:12 показывают номер текущего буфера, в который/из которого производиться обмен данными, об этом далее.
  • Биты 11:10 — допустимое количество ошибок. Данная таблица показывает, когда счетчик количества ошибок уменьшается:

image

Голова очереди

Голова очереди(Queue Head) имеет следующую структуру:

image

Queue Head Horizontal Link Pointer — указатель на следующую очередь, биты 2:1 имеют следующие значения в зависимости от типа очереди:

image

Endpoint Capabilities/Characteristics — характеристики очереди:

image

  • Биты 26:16 содержат максимальный размер пакета для передачи
  • Бит 14: Data Toggle Control — показывает, где хост-контроллер должен брать изначальное значение Data Toggle, 0 — игнорирует бит DT в qTD, сохраняет бит DT для головы очереди.
  • Бит 13:12 — характеристики скорости передачи:
  • Биты 11:8 — номер конечной точки, к которой выполняется запрос
  • Биты 6:0 — адрес устройства
  • Биты 29:23 — номер Хаба
  • Биты 22:16 — адрес Хаба

Переходим к самому интересному.

Драйвер EHCI

Начнем с того, какие запросы может выполнять EHCI. Есть 2 типа запросов: Control — а-ля команд, и Bulk — к конечным точкам, для обмена данными, к примеру, абсолютное большинство флешек(USB MassStorage) использует тип передачи данных Bulk/Bulk/Bulk. Мышь и клавиатура для передачи данных тоже используют Bulk — запросы.

Инициализируем EHCI и настраиваем асинхронную и последовательные очереди:


Собственно, код для сброса порта в изначальное состояние:


Control-запрос к устройству:


Код обработки очереди:


И теперь запрос к конечной точке(Bulk-запрос)


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

Хост-контроллер (HC) управляет передачей пакетов по шине. Использовались кадры в 1 миллисекунду. В начале каждого кадра хост-контроллер генерирует пакет начала кадра (SOF - Start of Frame).

Пакет SOF используется для синхронизации начала кадра и отслеживания количества кадров. Пакеты передаются с каждым кадром, как от хоста к устройству (исходящие) , так и от устройства к хосту (входящие) . Передачи всегда инициируются хостом (запрошенные передачи) . В силу этого может быть только один хост на шине USB. Каждая передача пакета имеет период статуса, в котором сторона, принимающая данные, может возвратить ACK (подтверждение приема) , NAK (повтор) , STALL (условная ошибка) или ничего (потерянный период данных, недоступное устройсто или отсоединение) . Раздел 8.5 Спецификации USB детально описывает пакеты. На шине USB могут произойти четыре различных типа передач: управляющая, основная, прерывание и изохронная. Типы передач и их характеристики описаны ниже (подраздел `Каналы').

Передачи больших объемов данных между устройством на шине USB и драйвером устройства делятся на множество пакетов хост-контроллером или драйвером HC.

Запросы устройства (управляющие передачи) к конечных точкам, используемым по умолчанию, являются специальными. Они состоят из двух или трех фаз: SETUP, DATA (oпциональная) и STATUS. Начтроечный пакет посылается устройству. Если есть фаза данных, то направление пакетов (или пакета) данных дается в настроечном пакете. Направление в фазе статуса противоположно направлению во время фазы данных. или IN если не было фазы данных. Оборудование хост-контроллера также дает регистры с текущим статусом корневых портов и изменений, которые случились с момента последнего сброса регистра изменения статуса. Доступ к этим регистрам дается через виртуализированных разветвитель, как и предполагается по спецификации USB [ 2]. Виртуальный разветвитель должен работать вместе с классом устройств-разветвителей, который описывается в 11 главе той спецификации. Он должен давать канал, испольщуемый по умолчанию, через который запросы устройств могут ему посылаться. Он возвращает набор дескрипторов, стандартных и специфичных для класса разветвителя. Он должен также давать канал прерываний, который сообщает об изменениях, произошедших на его портах. На данный момент для хост-контроллеров существуют две спецификации: Universal Host Controller Interface (UHCI; Intel) и Open Host Controller Interface (OHCI; Compaq, Microsoft, National Semiconductor). Спецификация UHCI разработана для уменьшения аппаратной сложности, требуя от драйвера хост-контроллера поддержки полного распределения передач для каждого кадра. Контроллеры типа OHCI гораздо более независимы, и дают более абстрактный интерфейс, выполняя много работы самостоятельно.

Читайте также: