Я хочу получить идентификатор поставщика и идентификатор устройства для подключенного USB-устройства через программу EFI. Я могу прочитать все пространство конфигурации PCI. Я нахожу хост-контроллер USB, к которому подключено мое USB-устройство. Я также могу прочитать всю память, адресованную этому контроллеру, но я не знаю, что именно я ищу в этой памяти, чтобы получить эти идентификаторы. Кто-нибудь может мне помочь?
Чтение идентификатора поставщика USB-устройства и идентификатора устройства из конфигурационного пространства PCI (EFI)
Ответы (3)
Протоколы USB определяют пакет настройки, который драйвер xHCI должен создать в xHCI, а оборудование xHCI преобразует его в USB - нет регистров, к которым он обращается напрямую для этой информации, как в случае с PCIe. Во-первых, я выложу всю процедуру перечисления USB-устройств на windows, которая отличается при использовании xHCI или eHCI.
Любые драйверы фильтра нижнего уровня, указанные в значении LowerFilters ключа перечисления устройства.
После аппаратного сброса все порты корневого концентратора будут отключены. Порт будет включен и будет ожидать подключения устройства. Когда оборудование обнаруживает подключение устройства, оно устанавливает флаги текущего состояния подключения и изменения состояния подключения в регистре PORTSC на 1, и это действие приведет к повышению уровня сигнала PSCEG одновременно с логическим ИЛИ в регистре PORTSC. биты. Этот сигнал генерирует событие изменения состояния порта в контроллере, которое заставляет оборудование контроллера xHCI помещать пакет (известный как блок запроса передачи) в кольцо событий. Сегменты и таблица Event Ring выделяются из невыгружаемого пула и инициализируются во время перечисления контроллера xHCI, вероятно, во время процедуры StartDevice
драйвера xHCI, который вызывается при его загрузке; он также инициализирует регистры кольца событий в пространстве MMIO устройства.
Постановка события в кольцо заставляет оборудование запускать прерывание по определенному смещению в таблице MSI-X (смещение MSI-X от BAR и номера BAR хранятся в возможности MSI-X в пространстве конфигурации PCIe контроллер xHCI; следовательно, таблица MSI-X находится в пространстве MMIO). Первичное кольцо событий всегда получает все события изменения статуса порта. Первичное кольцо событий всегда отображается на первое прерывание MSI-X. Прерывание MSI-X будет передаваться как стандартный PCIe MSI к LAPIC, и оно будет ставить прерывание в очередь в IRR для вектора, указанного в данных хранилища таблицы MSI-X. Драйвер xHCI ранее зарегистрировал ISR в записи IDT, соответствующей вектору, с использованием API ядра и HAL. ISR драйвера xHCI понимает, что пакет TRB является событием изменения статуса порта; он может оценить идентификатор порта, чтобы определить порт корневого концентратора, который был источником события изменения (например, 5), а затем проверить 5-й регистр PORTSC, чтобы увидеть, какое изменение имело место, к которому он обращается при определенном смещении от операционной базы , который представляет собой смещение от адреса в полосе BAR конфигурационного пространства PCIe контроллера xHCI, которое было выделено во время перечисления PCIe при загрузке (база MMIO); формула Operational base + (400h + (10h*(n-1)))
, где n - номер порта от 1 до MaxPorts, а Operational base - это значение в регистре CAPLENGTH + база MMIO.
Драйвер xHCI уведомляет драйвер корневого концентратора о прибытии устройства (я думаю, вызывая Функция обратного вызова корневого концентратора, к которой он, возможно, обращается через PDO корневого концентратора), а драйвер корневого концентратора создает для него PDO и уведомляет диспетчер PnP, что набор дочерних устройств для корневого концентратора изменился. . Либо драйвер xHCI автоматически назначает идентификатор слота и молча выполняет процедуру TRB адресного устройства перед вызовом функции обратного вызова и предоставляет идентификатор слота на месте, либо драйвер концентратора должен инициировать это, отправив URB контроллеру xHCI для запроса Идентификатор слота назначается и возвращается ему, когда он получает информацию об изменении статуса порта для определенного идентификатора порта (я не уверен. И я не уверен, какие структуры данных контролируются драйвером концентратора, а не xHCI драйвер, так что это предположение). Когда драйвер xHCI получает URB, он отправляет разрешающий слот TRB в кольце команд и получает идентификатор слота из TRB завершения команды в кольце событий; он выделит структуру контекста устройства (известную как контекст устройства вывода) и указатель на нее в массиве базы контекста устройства, который контроллер xHCI поддерживает в невыгружаемом пуле. В ответе URB драйвер концентратора получает идентификатор слота; затем драйвер концентратора выделяет в памяти контекст устройства ввода. Флаги добавления контекста для контекста слота и контекста конечной точки 0 в структуре контекста управления вводом в контексте устройства ввода установлены на 1, чтобы указать, что они должны быть добавлены. Затем контексту слота в структуре контекста устройства ввода присваивается номер порта, корневая строка и количество конечных точек. Структура данных контекста конечной точки 0 (также известная как элемент управления по умолчанию) во входном контексте должна быть сконфигурирована с допустимыми значениями для указателя TR Dequeue (указывает на выделяемое им кольцо передачи), типа EP, количества ошибок и полей максимального размера пакета. Значения MaxPStreams, Max Burst Size и EP state должны быть установлены в 0. Указатель структуры Input Context отправляется концентратором в команде устройства адресации, адресованной идентификатору слота нового устройства, которая отправляется через URB на xHCI. Драйвер, и он помещает адресное устройство TRB в кольцо команд, а хост-контроллер копирует входной контекст в выходной контекст, на который указывает запись DCBA для слота.
Затем драйвер концентратора отправляет URB драйверу xHCI, чтобы получить дескриптор устройства в следующей форме:
Status = SubmitRequestToRootHub(RootHubDeviceObject,
IOCTL_INTERNAL_USB_SUBMIT_URB,
Urb,
NULL);
Все URB, отправленные после того, как идентификатор слота возвращается концентратору, будут содержать идентификатор слота. Он также свяжет ChildDeviceObject->DeviceExtension->UsbDeviceHandle
с Urb->UrbHeader.UsbdDeviceHandle
, что сделает PDO концентратором, выделенным для нового устройства, доступным через URB. RootHubDeviceObject
- это PDO драйвера концентратора, который принадлежит драйверу контроллера xHCI (или паре usbxhci-usbport), который будет использоваться при вызове IoCallDriver
внутри этой процедуры. URB будет типа GET_DESCRIPTOR
. Затем IRP инициализируется основным кодом IRP_MJ_INTERNAL_DEVICE_CONTROL
, а местоположение стека инициализируется с помощью URB в качестве одного из параметров и StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
. Затем он вызывает IoCallDriver
на RootHubDeviceObject
, который принадлежит драйверу xHCI.
Затем драйвер xHCI индексирует массив дверных звонков, используя идентификатор слота, и записывает последовательность в регистр дверного звонка по этому индексу, что указывает на то, что указатель постановки в очередь Control EP 0 был обновлен. Затем хост-контроллер начинает действовать и считывает TRB, увеличивая указатель удаления из очереди; и, когда он равен указателю включения в очередь, он останавливается. Для каждого TRB он отправляет соответствующий пакет устройству. Когда он обрабатывает стадию состояния TRB, это вызовет прерывание в кольце событий (я думаю, что кольцо 0), которое вызывает прерывание MSI-x, как указано ранее, для LAPIC процессора на указанный вектор, который будет выбран. вверх с помощью xHCI Contoller ISR и DPC. ISR развернет DPC, который завершит IRP. Дескриптор будет по виртуальному адресу, указанному в IRP URB драйвером концентратора.
Драйвер концентратора вставляет информацию, полученную в IRP URB, в поле PDO->DeviceExtension
, которое является указателем на определенную драйвером структуру, с которой он может делать то, что он хочет, что означает, что информация по существу кэшируется и больше не нужно отправлять URB. к драйверу xHCI. Концентратор также отправляет URB GET_CONFIGURATION в драйвер xHCI для каждого номера конфигурации, указанного устройством в дескрипторе устройства. Затем драйвер xHCI передаст это значение конфигурации устройству в TRB GET_CONFIGURATION с правильным номером конфигурации, и вся иерархия конфигурации для этого номера конфигурации будет возвращена драйверу концентратора по адресу, указанному в URB. Затем он вызывает IoInvalidateDeviceRelations()
с параметром типа BusRelations
и указателем на его PDO (объект физического устройства), который был назначен драйвером xHCI. Менеджер PnP запрашивает у стека устройств PDO текущий список устройств на шине, используя запрос IRP_MN_QUERY_DEVICE_RELATIONS
; для этого он инициализирует структуру IRP (в идеале повторно использует одну из альтернативного списка на основе stacksize
подсказки в объекте устройства; в противном случае он напрямую выделяет память из невыгружаемого пула для нового). IRP указывает на стек (который примыкает к IRP) через член CurrentStackLocation
. Затем он инициализирует первое расположение стека для вызова, который он хочет выполнить (в этом случае основная функция IRP_MJ_PNP
и второстепенная функция IRP_MN_QUERY_DEVICE_RELATIONS
). Затем он вызывает драйвер наверху стека устройств отправленного PDO, который может быть драйвером верхнего фильтра (который просто не будет реализовывать эту второстепенную функцию, а тело функции будет кодом для ее передачи вниз - мы предположим пока его нет). Таким образом, вершиной стека будет FDO концентратора (который он достигает с помощью IoGetAttachedDevice, который находится на вершине стека). Он вызывает его, используя IoCallDriver(*FDO, *IRP)
, оболочку IofCallDriver
, которая получает следующее местоположение стека путем уменьшения указателя CurrentStackLocation
, что заставляет его указывать на следующее местоположение стека по правилам арифметики указателя C (которое является первым местоположением стека как указатель был инициализирован один за ним), а затем он использует основной номер функции IRP_MJ_PNP
, указанный в местоположении стека, для индексации в массив MajorFunction
объекта драйвера FDO, который был передан в IoCallDriver
(драйвер концентратора), и вызывает функцию в этой позиции в массиве.
Код этого вызова выглядит так:
Вы можете видеть, что он проходит IRP. Это позволяет обработчику функций драйвера концентратора USB для IRP_MJ_PNP
проверять второстепенную функцию в текущем местоположении стека, а затем вызывать правильную внутреннюю функцию. Для каждого дочернего устройства обработчик ссылается на PDO в структуре DEVICE_RELATIONS, которая представляет собой просто массив указателей PDO. Затем он устанавливает Irp->IoStatus.Information
в указатель на массив и возвращается. Затем Plug and Play Manager просматривает массив PDO и сравнивает адреса с адресами PDO в дереве устройств, которые он уже перечислил. Если есть новые адреса, он запрашивает идентификаторы устройства и экземпляра, а также требования к ресурсам; и, если какой-либо из PDO был помечен как неактивный, он также отправляет IRP_MN_SURPRISE_REMOVAL
этим PDO, используя тот же процесс инициализации IRP, как описано ранее (FDO устройств не будут реализовывать функцию неожиданного удаления и передавать его драйверу концентратора. ), а драйвер концентратора отключит устройство и освободит назначенные ему аппаратные ресурсы.
return FDO->DriverObject->MajorFunction[StackPtr->MajorFunction](FDO,
Irp);
Чтобы запросить идентификаторы устройства и экземпляра, PnP Manager отправляет IRP_MN_QUERY_ID
(один для идентификатора устройства и отдельный для идентификатора экземпляра) каждому PDO в массиве, указатель которого он получил, который является новым (который будет PDO для нового устройства. который был создан драйвером корневого концентратора). Для IRP, который запрашивает идентификатор устройства (идентификатор устройства представляет собой зависящее от шины соединение Windows, состоящее из идентификатора устройства + идентификатора поставщика + идентификатора подсистемы + версии идентификатора поставщика подсистемы, который он получает от устройства, и префикса шины, также известного как перечислитель, например USB), он отправляет IRP_MN_QUERY_ID
, но инициализирует Parameters.QueryId.IdType
член расположения стека значением BusQueryDeviceID
. В ответ на запрос идентификатора устройства драйвер концентратора должен запросить у устройства информацию, необходимую для создания и объединения идентификатора устройства с помощью префикса шины, но он уже сделал это, как только был создан PDO, поэтому он может просто использовать DeviceExtension
, в который была вставлена информация. Идентификатор экземпляра - это строка идентификации устройства, которая отличает устройство от других устройств того же типа, и, вероятно, использует значение iSerialNumber
в дескрипторе USB или простое приращение - это зависит от шины. Вместе они образуют DIID (идентификатор экземпляра устройства). InstanceID запрашивается диспетчером PnP в отдельном вызове после использования Parameters.QueryId.IdType = BusQueryInstanceID
в IRP.
Драйвер концентратора получает PDO, запрошенный диспетчером PnP через обработчик IRP_MJ_PNP, установленный на DriverEntry, и теперь создает DIID, используя поля в дескрипторе устройства, который ранее был проанализирован и вставлен в DeviceExtension
PDO, где usDeviceId
выглядит как уже связанный префикс + ProductID + Vendor. Помните, что DIID - это Bus + Device ID + Instance ID, а Device ID - Bus + Vendor + Product ID. Однако он выполняет некоторые фиксированные операции с этими идентификаторами, меняя их все на стандартный формат usbhub, распознаваемый файлами usbstor .inf. DIID будет выглядеть так: USB\VID_<num>&PID_<num>\<InstanceID>
. Только PDO имеют DIID или Hardware / Compatible ID.
Затем диспетчер PnP использует возвращенный DIID для индексации в реестре по адресу HKLM\SYSTEM\CurrentControlSet\Enum\Bus\DeviceID\InstanceID
.
В нем значение classguid, которое ведет к подключу класса в HKLM\SYSTEM\CurrentControlSet\Control\Class\<GUID>
, который может быть, например, классом клавиатуры. Эти значения заполняются файлами .INF драйвера.
Диспетчер PnP проверяет реестр на наличие соответствующего функционального драйвера, и когда он не находит его, он сообщает диспетчеру PnP пользовательского режима о новом устройстве по его DIID. PnP Manager пользовательского режима сначала пытается выполнить автоматическую установку без вмешательства пользователя. Если процесс установки включает размещение диалоговых окон, требующих взаимодействия с пользователем, и текущий пользователь, вошедший в систему, имеет права администратора, PnP-менеджер пользовательского режима запускает приложение Rundll32.exe (то же приложение, в котором размещены утилиты Панели управления) для выполнения Мастер установки оборудования (% SystemRoot% \ System32 \ Newdev.dll). Если текущий вошедший в систему пользователь не имеет прав администратора (или если пользователь не вошел в систему) и для установки устройства требуется взаимодействие с пользователем, PnP-менеджер пользовательского режима откладывает установку до тех пор, пока не войдет в систему привилегированный пользователь. Мастер установки оборудования использует функции API Setupapi.dll и CfgMgr32.dll (диспетчер конфигурации) для поиска файлов INF, которые соответствуют драйверам, совместимым с обнаруженным устройством.
Он выбирает файл .INF, который наиболее похож на него, присваивая ему рейтинг, выполнив поиск Совместимые идентификаторы в файле .INF, которые, как мы надеемся, соответствуют идентификаторам оборудования / совместимости, сгенерированным из DIID, которые были вставлены в расширение устройства. Если он его находит, он устанавливает драйвер. Установка будет происходить с каждым новым подключенным устройством и, по сути, представляет собой просто заполнение реестра правильной информацией под этим DIID.
Установка выполняется в два этапа. На первом этапе сторонний разработчик драйверов импортирует пакет драйвера в хранилище драйверов, а на втором этапе система выполняет фактическую установку, которая всегда выполняется с помощью процесса% SystemRoot% \ System32 \ Drvinst.exe.
Управление возвращается диспетчеру PnP, и он использует ключи реестра для загрузки драйверов в следующем порядке:
Драйверы USB будут иметь промежуточный узел разработки - usbccgp, если это составное устройство, и usbstor, если это запоминающее устройство большой емкости, что можно увидеть здесь. Когда драйвер концентратора отправляет DIID, диспетчер PnP загружает именно usbstor, как показано на изображении выше. (Нам нужен промежуточный USB-узел хранения, чтобы транслировать общие IRP disk.sys в URB и обрабатывать конфигурацию USB-накопителя, а не заполнять все функции в usbhub.sys).
- Любые драйверы фильтра нижнего уровня, указанные в значении LowerFilters ключа класса устройства.
- Функциональный драйвер, указанный значением Service в ключе перечисления устройства. Это значение интерпретируется как ключ драйвера в HKLM \ SYSTEM \ CurrentControlSet \ Services.
- Любые драйверы фильтров верхнего уровня, указанные в значении UpperFilters ключа перечисления устройства.
- Любые драйверы фильтров верхнего уровня, указанные в значении UpperFilters ключа класса устройства.
- В пространстве конфигурации PCI отображаются устройства PCI и PCI Express, а не USB-устройства.
Он загружает драйверы и вызывает DriverEntry
функцию каждого, а затем AddDevice
процедуру, если они еще не запущены (для другого USB-устройства, использующего тот же драйвер); в противном случае он просто вызывает процедуру AddDevice
. Подпрограмма AddDevice
создает FDO для переданного PDO. В своей AddDevice
процедуре драйвер фильтра создает объект устройства фильтра (FiDO) и присоединяет его к стеку устройств (IoAttachDeviceToDeviceStack). Затем PnP Manager создает узел устройства и связывает его с PDO. PnP Manager уже ранее получил мнение шинного устройства (драйвера концентратора) о ресурсах устройства, используя IRP_MN_QUERY_RESOURCE_REQUIREMENTS
одновременно с отправкой пакетов IRP для идентификатора устройства. Диспетчер PnP теперь отправляет IRP_MN_FILTER_RESOURCE_REQUIREMENTS
, используя IoCallDriver
в верхней части нового узла устройства с указанным FDO. Только драйвер FDO обрабатывает это, и он изменит любые требования к объекту устройства, который ему нужен, который драйвер концентратора не смог предсказать. Запоминающее устройство USB не требует прерывания, так как оно будет использовать только первичное кольцо событий. Если да, то в ответе на IRP он должен указать количество требуемых сообщений MSI-x), и как только диспетчер PnP назначает ресурсы устройству, он отправляет IRP_MN_START_DEVICE
IRP в стек устройств. Хотя каждое USB-устройство может иметь отдельное прерывание и соответствующее кольцо событий, на самом деле это не имеет значения, потому что на прерывания всегда будет реагировать один и тот же драйвер самого низкого уровня: драйвер xHCI; USB-устройства не имеют регистров ISR для регистрации. Следовательно, все USB-устройства могут использовать одно кольцо событий и одно прерывание.
В подпрограмме usbstor, которая обрабатывает IRP запуска устройства, IRP передается на устройство шины (usbhub) после того, как процедура устанавливает IoCompletionRoutine
. IoCompletionRoutine
, когда он в конечном итоге вызывается, отправит URB GET_CONFIGURATION, который будет передан его PDO (принадлежащему usbhub), а usbhub представит конфигурацию, которую он кэшировал в этом расширении устройства PDO ранее. Usbstor в конечном итоге решает, какую конфигурацию использовать, и отправляет SET_CONFIGURATION URB. Usbhub заполнит входной контекст из кэшированной конфигурации конечными точками, то есть ISOCH IN, INTERRUPT IN, и добавит указатель входного контекста в URB. Затем Usbhub добавляет дополнительную информацию, такую как идентификатор слота, и отправляет URB контроллеру xHCI, а тот забирает его и вставляет TRB в кольцо передачи конечной точки 0 по умолчанию для устройства, на которое хост-контроллер будет заполнять контекст устройства вывода слота с помощью контекст устройства ввода в соответствии с контекстом управления вводом и информирует устройство о выбранной конфигурации.
Затем Usbstor выделит PDO и вызовет IoInvalidateBusRelations
. Когда он доберется до него, usbstor запросит информацию об устройстве, в котором ранее хранился его PDO, принадлежащий usbhub, и он переведет DIID в стандартный формат для usbstor, распознаваемый драйверами disk.sys .inf (формат \ Disk & Ven_xxx & Prod_xxx & Rev_x.xx) и добавит префикс USBSTOR \, и это позволит реестру загружать disk.sys и partmgr.sys в качестве фильтра, хранящегося в подразделе класса disk.sys. Usbstor теперь является устройством шины для устройства.
Disk.sys FDO проверит таблицу поддерживаемых драйверов, чтобы узнать, сколько дисков (N) было пронумеровано в системе, и назовет FDO \Device\HarddiskN\DRN
. Partmgr.sys создает символическую ссылку \Device\HarddiskN\Partiton0
на \Device\HarddiskN\DRN
. Затем Partmgr вызовет IoReadPartitionTableEx
и создаст PDO разделов для каждого раздела, назвав их \Device\HarddiskN\PartitonM
и так далее. Для каждого раздела он отправляет IOCTL_INTERNAL_VOLMGR_PARTITION_ARRIVED
IRP в volmgr и предоставляет подпись диска и смещение раздела. Volmgr создает PDO тома для каждого тома и создает символические ссылки между \Device\HarddiskN\PartitonM
и именем PDO тома \Device\HarddiskvolumeX
X >= 1
, которое он назначает PDO тома (фактически из-за этой символической ссылки PDO раздела с таким именем никогда не будет доступен, а сам PDO не имеет devnode и внутренне управляется partmgr) и отправляет MntMgr запрос IRP_MJ_DEVICE_CONTROL
, указывая IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION
путем вызова IoBuildDeviceIoControlRequest для каждого PDO тома (Harddiskvolume1 всегда является первым томом на disk.sys \ Device \ Harddisk0 \ DR0). Диспетчер монтирования отвечает, запрашивая у volmgr непостоянное имя объекта устройства тома (отправляя 3 IOCTL (контрольные пакеты IRP)), расположенные в каталоге устройства дерева системных объектов (например: \ Device \ HarddiskVolume1), уникальный идентификатор, сгенерированный для том и предлагаемое постоянное имя символической ссылки, например \DosDevices\D:
.
Постоянная буква диска и точки подключения хранятся с идентичными полями данных. Данные значений называются уникальным идентификатором, который volmgr предоставляет mntmgr с IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
. Уникальный идентификатор базовых дисков - это подпись и смещение раздела. Диспетчер монтирования использует предложенное имя, если база данных диспетчера монтирования еще не содержит постоянного буквенного имени диска для тома, сопряженного с уникальным идентификатором. В противном случае он игнорирует предложение и использует имя буквы диска в своей постоянной базе данных имен. Он связывает \DosDevices\D:
с уникальным идентификатором и разрабатывает идентификатор GUID и связывает идентификатор GUID в форме \??\Volume{GUID}
с уникальным идентификатором в своем пространстве имен, а затем создает между ними символические связи пространства имен диспетчера объектов. Более поздние точки монтирования, то есть \DosDevices\D:\mymount
, также будут связаны с уникальным идентификатором. На фотографии выше диск MBR содержит разделы A: (зарезервировано системой) и C:, и ясно, что у них одинаковая подпись MBR и другое смещение. Диск D: является диском GPT и имеет подпись и смещение GPT. E: это USB-накопитель MBR со специальной подписью и смещением USBSTOR.
Когда файл на C: открывается в первый раз, файловая система не будет смонтирована, поэтому, когда строка пути к файлу анализируется в IopParseDevice
, который вызывает IopCheckVpbMounted
на томе (C: соответствует имени устройства \Device\HarddiskVolume2
из-за символического ссылка, созданная менеджером монтирования), он вызовет IopMountDevice
потому что VPB->DeviceObject == NULL
, который отправляет IRP_MJ_FILE_SYSTEM_CONTROL/IRP_MN_MOUNT_VOLUME
каждой зарегистрированной файловой системе, зарегистрированной с помощью IoRegisterFileSystem
. Вызываемые FS обрабатывают IRP_MN_MOUNT_VOLUME
и определяют, находится ли их файловая система на носителе, и, если это так, файловая система создает объект тома файловой системы (VDO) и помещает его в VPB. C:
указывает устройство, а \file
- объект файла. \
- объект корневого каталога. IoGetRelatedDeviceObject
получает вершину стека VDO из файлового объекта (выполняет IoGetAttachedDevice
на FileObject->Vpb->DeviceObject
).
Внутренняя структура драйвера NTFS:
Дерево устройств USB-накопитель Windows
Как перечисляется контроллер xHCI
При загрузке системы pci.sys загружается apci.sys, и одна из вещей, которые он выполняет, - это вызов IoInvalidateDeviceRelations
, который запускает диспетчер PnP для отправки IRP_MN_QUERY_DEVICE_RELATIONS
, на который он отвечает списком PDO. Он создает список на месте, используя поле Base address of enhanced configuration mechanism
таблицы MCFG
ACPI для получения основы пространства конфигурации PCIe (PCIEXBAR), а затем выполняет итерацию по нему на границах 4096 байт и для любых идентификаторов поставщика / устройства, которые он находит на границах, он создает PDO и связывает его с номером конфигурации. Одним из устройств будет контроллер xHCI. Он проходит точно такой же процесс, как описано выше, в конечном итоге создает DIID, проверяет реестр и т. Д., Что приводит к загрузке драйвера xHCI; и диспетчер PnP также запрашивает у шины ресурсы, которые потребуются его дочернему элементу, с IRP_MN_QUERY_RESOURCE_REQUIREMENTS
в половину стека PDO (который также будет обрабатываться фильтром шины ACPI). После загрузки драйвера xHCI он отправляет IRP_MN_FILTER_RESOURCE_REQUIREMENTS
драйверу xHCI, чтобы он мог внести изменения в требования к ресурсам. Devnode PDO контроллера xHCI получает IRP_MN_START_DEVICE
, и контроллер xHCI замечает, что это для его собственного PDO, устанавливает IoCompletionRoutine
и передает его pci.sys, который увидит, что переданный PDO является дочерним, и выделит физический диапазон MMIO, определенный менеджером PnP, который он получил в списке ресурсов в параметрах IRP начального устройства в BAR, а также устанавливает прерывание MSI-x, определенное в IRP_MN_QUERY_RESOURCE_REQUIREMENTS
и IRP_QUERY_FILTER_RESOURCE_REQUIREMENTS
, и вызывает IoCompleteRequest
, который вызывает IoCompletionRoutine
xHCI Набор драйверов, который будет вызывать MmMapIoSpace
дескрипторы CmResourceTypeMemory в CM_RESOURCE_LIST в Parameters.StartDevice.AllocatedResourcesTranslated. Он создаст кольцо событий, кольцо команд и DBCA в виртуальном адресном пространстве, полученном от MmMapIoSpace
, и установит регистры в пространстве MMIO, чтобы они указывали на них. Затем он связывает кольцо событий с вектором MSI-x, полученным в IRP_MN_START_DEVICE
. Затем он настроит ISR с использованием IoConnectInterrupt и регистрация DPC. Я не уверен, как загружается драйвер USB-концентратора, но потенциально это делается в StartDevice
драйвера xHCI; он может вызвать IoInvalidateDeviceRelations
и сказать, что у него есть только один дочерний элемент, Hub. Он предоставляет DIID, прикрепленный к IUSB3\
.
xHCI
Пространство конфигурации PCI покажет вам производителя и идентификатор устройства USB-контроллера, но не подключенные устройства. Вам нужно будет пронумеровать шину USB, читая / записывая для этого регистры USB.
Обратите внимание, что захват USB-контроллера приведет к поломке работающего в данный момент USB-стека и отключению вашей USB-клавиатуры и загрузочных устройств.
Если вы работаете в оболочке UEFI, возможно, вы найдете то, что вам нужно, в выводе devtree.
Если вы пишете свой собственный код UEFI DXE, он должен будет запросить драйвер USB.
Несмотря на то, что на вопрос уже был дан ответ и помечен как принятый, я просто хотел бы помахать флагом для использования:
Таким образом, ваше приложение будет переносимым между всеми совместимыми платформами UEFI.
EFI_USB_IO_PROTOCOL
для взаимодействия с USB-устройствами независимо от того, к какой шине подключен хост-контроллер.- Этот ответ - демонстрация силы. Высоко оценен!
Пользователь @fpmurphy, который публикует здесь ответы, иногда имеет примеры обоих в своей области github..
EFI_PCI_IO_PROTOCOL
для операций PCI