Выбор конфигурации для USB-устройства
Чтобы выбрать конфигурацию для USB-устройства, драйвер клиента для устройства должен выбрать по крайней мере одну из поддерживаемых конфигураций и указать альтернативные параметры каждого интерфейса для использования. Драйвер клиента упаковает эти варианты в запрос на выборку конфигурации и отправляет запрос в предоставленный Корпорацией Майкрософт стек USB-драйверов, в частности драйвер шины USB (PDO-концентратор USB). Драйвер шины USB выбирает каждый интерфейс в указанной конфигурации и настраивает канал связи для каждой конечной точки в интерфейсе. После выполнения запроса драйвер клиента получает дескриптор для выбранной конфигурации и дескриптор конвейера для конечных точек, определенных в активном альтернативном параметре для каждого интерфейса. Затем драйвер клиента может использовать полученные дескрипторы для изменения параметров конфигурации и отправки запросов ввода-вывода на чтение и запись в определенную конечную точку.
Драйвер клиента отправляет запрос на выборку конфигурации в блоке запросов USB (URB) типа URB_FUNCTION_SELECT_CONFIGURATION. Процедура в этом разделе описывает, как использовать подпрограмму USBD_SelectConfigUrbAllocateAndBuild для создания этой urb. Подпрограмма выделяет память для URB, форматирует URB для запроса на выборку конфигурации и возвращает адрес URB драйверу клиента.
Кроме того, можно выделить структуру URB , а затем отформатировать urb вручную или путем вызова макроса UsbBuildSelectConfigurationRequest .
Предварительные требования
Начиная с Windows 8 USBD_SelectConfigUrbAllocateAndBuild заменяет USBD_CreateConfigurationRequestEx.
Перед отправкой запроса на выборку конфигурации необходимо иметь дескриптор USBD для регистрации драйвера клиента в стеке драйверов USB. Чтобы создать дескриптор USBD , вызов USBD_CreateHandle.
Убедитесь, что вы получили дескриптор конфигурации (USB_CONFIGURATION_DESCRIPTOR структуру) выбранной конфигурации. Как правило, вы отправляете URB типа URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (см . _URB_CONTROL_DESCRIPTOR_REQUEST) для получения сведений о конфигурации устройства. Дополнительные сведения см. в разделе Дескрипторы конфигурации USB.
Шаг 1. Создание массива структур USBD_INTERFACE_LIST_ENTRY
Получение количества интерфейсов в конфигурации. Эти сведения содержатся в элементе bNumInterfacesструктуры USB_CONFIGURATION_DESCRIPTOR .
Создание массива USBD_INTERFACE_LIST_ENTRY структур. Число элементов в массиве должно быть на один больше числа интерфейсов. Инициализируйте массив, вызвав RtlZeroMemory.
Драйвер клиента задает альтернативные параметры в каждом интерфейсе для включения в массиве USBD_INTERFACE_LIST_ENTRY структур.
- Элемент InterfaceDescriptor каждой структуры указывает на дескриптор интерфейса, содержащий альтернативный параметр.
- Элемент Интерфейса каждой структуры указывает на структуру USBD_INTERFACE_INFORMATION , которая содержит сведения о канале в элементе Pipe . Каналы хранят сведения о каждой конечной точке, определенной в альтернативном параметре.
Получите дескриптор интерфейса для каждого интерфейса (или его альтернативного параметра) в конфигурации. Эти дескрипторы интерфейса можно получить, вызвав USBD_ParseConfigurationDescriptorEx.
Сведения о драйверах-функциях для составного USB-устройства:
Если USB-устройство является составным, конфигурация выбирается предоставленным Корпорацией Майкрософт универсальным родительским драйвером USB (Usbccgp.sys). Драйвер клиента, который является одним из драйверов функций составного устройства, не может изменить конфигурацию, но драйвер по-прежнему может отправить запрос на выборку конфигурации через Usbccgp.sys.
Перед отправкой этого запроса драйвер клиента должен отправить запрос URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. В ответ Usbccgp.sys получает частичный дескриптор конфигурации , содержащий только дескрипторы интерфейса и другие дескрипторы, относящиеся к конкретной функции, для которой загружается драйвер клиента. Число интерфейсов, указанных в поле bNumInterfaces частичного дескриптора конфигурации, меньше общего числа интерфейсов, определенного для всего составного USB-устройства. Кроме того, в частичном дескрипторе конфигурации bInterfaceNumber дескриптора интерфейса указывает фактический номер интерфейса относительно всего устройства. Например, Usbccgp.sys может сообщать о частичном дескрипторе конфигурации со значением bNumInterfaces 2 и значением bInterfaceNumber 4 для первого интерфейса. Обратите внимание, что номер интерфейса больше, чем количество интерфейсов, сообщаемых.
При перечислении интерфейсов в частичной конфигурации старайтесь не искать интерфейсы, вычисляя номера интерфейсов на основе количества интерфейсов. В предыдущем примере, если USBD_ParseConfigurationDescriptorEx вызывается в цикле, который начинается с нуля, заканчивается на
(bNumInterfaces - 1)
и увеличивает индекс интерфейса (указанный в параметре InterfaceNumber ) в каждой итерации, подпрограмме не удается получить правильный интерфейс. Вместо этого убедитесь, что вы ищете все интерфейсы в дескрипторе конфигурации, передав -1 в поле InterfaceNumber. Дополнительные сведения о реализации см. в примере кода в этом разделе.Сведения о том, как Usbccgp.sys обрабатывает запросы на выборку конфигурации, отправленные драйвером клиента, см. в разделе Настройка Usbccgp.sys для выбора конфигурации USB по умолчанию.
Для каждого элемента (кроме последнего) в массиве задайте для элемента InterfaceDescriptor адрес дескриптора интерфейса. Для первого элемента массива задайте для члена InterfaceDescriptor адрес дескриптора интерфейса, представляющего первый интерфейс в конфигурации. Аналогичным образом для n-гоэлемента в массиве задайте для члена InterfaceDescriptor адрес дескриптора интерфейса, который представляет n-йинтерфейс в конфигурации.
Элемент InterfaceDescriptor последнего элемента должен иметь значение NULL.
Шаг 2. Получение указателя на URB, выделенную стеком драйверов USB
Затем вызовите USBD_SelectConfigUrbAllocateAndBuild , указав конфигурацию для выбора и заполненный массив USBD_INTERFACE_LIST_ENTRY структур. Подпрограмма выполняет следующие задачи:
Создает URB и заполняет его сведениями об указанной конфигурации, ее интерфейсах и конечных точках, а также задает тип запроса URB_FUNCTION_SELECT_CONFIGURATION.
В этой urb выделяет структуру USBD_INTERFACE_INFORMATION для каждого дескриптора интерфейса, указанного драйвером клиента.
Задает элемент Interfacen-гоэлемента массива USBD_INTERFACE_LIST_ENTRY , предоставленного вызывающим объектом, адрес соответствующей структуры USBD_INTERFACE_INFORMATION в URB.
Инициализирует InterfaceNumber, AlternateSetting, NumberOfPipes, Pipes[i]. MaximumTransferSize и Pipes[i]. Элементы PipeFlags .
Примечание
В Windows 7 и ealier драйвер клиента создал URB для запроса на выборку конфигурации, вызвав USBD_CreateConfigurationRequestEx. В Windows 2000 USBD_CreateConfigurationRequestEx инициализирует Pipes[i]. MaximumTransferSize до максимального размера передачи по умолчанию для одного запроса на чтение и запись URB. Драйвер клиента может указать другой максимальный размер передачи в каналах[i]. MaximumTransferSize. Стек USB игнорирует это значение в Windows XP, Windows Server 2003 и более поздних версиях операционной системы. Дополнительные сведения о MaximumTransferSize см. в разделе Настройка передачи USB и размеров пакетов статьи Выделение пропускной способности USB.
Шаг 3. Отправка URB в стек драйверов USB
Чтобы отправить URB в стек драйверов USB, драйвер клиента должен отправить IOCTL_INTERNAL_USB_SUBMIT_URB запрос на управление вводом-выводом . Сведения об отправке URB см. в разделе Отправка URB.
После получения URB стек USB-драйверов заполняет остальные элементы каждой структуры USBD_INTERFACE_INFORMATION . В частности, элемент массива Pipes заполняется сведениями о каналами, связанными с конечными точками интерфейса.
Шаг 4. После завершения запроса проверьте структуры USBD_INTERFACE_INFORMATION и URB.
После того как стек usb-драйвера завершит IRP для запроса, стек возвращает список альтернативных параметров и связанных интерфейсов в массиве USBD_INTERFACE_LIST_ENTRY .
Элемент "Каналы " каждой структуры USBD_INTERFACE_INFORMATION указывает на массив USBD_PIPE_INFORMATION структур, содержащих сведения о канале, связанном с каждой конечной точкой этого интерфейса. Драйвер клиента может получать дескрипторы канала из pipe[i]. PipeHandle и используйте их для отправки запросов ввода-вывода в определенные каналы. Каналы[i]. Элемент PipeType указывает тип конечной точки и передачи, поддерживаемых этим каналом.
В элементе UrbSelectConfiguration urb стек драйверов USB возвращает дескриптор, который можно использовать для выбора альтернативного параметра интерфейса, отправляя другой URB типа URB_FUNCTION_SELECT_INTERFACE (запрос на выбор интерфейса). Чтобы выделить и создать структуру URB для этого запроса, вызовите USBD_SelectInterfaceUrbAllocateAndBuild.
Запросы select-configuration и select-interface могут завершиться ошибкой, если не хватает пропускной способности для поддержки изохронных конечных точек, конечных точек управления и прерываний в включенных интерфейсах. В этом случае драйвер шины USB устанавливает для элемента Status заголовка URB значение USBD_STATUS_NO_BANDWIDTH.
В следующем примере кода показано, как создать массив структур USBD_INTERFACE_LIST_ENTRY и вызвать USBD_SelectConfigUrbAllocateAndBuild. В примере запрос отправляется синхронно путем вызова SubmitUrbSync. Пример кода для SubmitUrbSync см. в статье Отправка URB.
/*++
Routine Description:
This helper routine selects the specified configuration.
Arguments:
USBDHandle - USBD handle that is retrieved by the
client driver in a previous call to the USBD_CreateHandle routine.
ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.
Return Value: NT status value
--*/
NTSTATUS SelectConfiguration (PDEVICE_OBJECT DeviceObject,
PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION nextStack;
PIRP irp;
PURB urb = NULL;
KEVENT kEvent;
NTSTATUS ntStatus;
PUSBD_INTERFACE_LIST_ENTRY interfaceList = NULL;
PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor = NULL;
PUSBD_INTERFACE_INFORMATION Interface = NULL;
USBD_PIPE_HANDLE pipeHandle;
ULONG interfaceIndex;
PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
// Allocate an array for the list of interfaces
// The number of elements must be one more than number of interfaces.
interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
NonPagedPool,
sizeof(USBD_INTERFACE_LIST_ENTRY) *
(deviceExtension->NumInterfaces + 1));
if(!interfaceList)
{
//Failed to allocate memory
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
// Initialize the array by setting all members to NULL.
RtlZeroMemory (interfaceList, sizeof (
USBD_INTERFACE_LIST_ENTRY) *
(deviceExtension->NumInterfaces + 1));
// Enumerate interfaces in the configuration.
for ( interfaceIndex = 0;
interfaceIndex < deviceExtension->NumInterfaces;
interfaceIndex++)
{
interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
ConfigurationDescriptor,
StartPosition, // StartPosition
-1, // InterfaceNumber
0, // AlternateSetting
-1, // InterfaceClass
-1, // InterfaceSubClass
-1); // InterfaceProtocol
if (!interfaceDescriptor)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
// Set the interface entry
interfaceList[interfaceIndex].InterfaceDescriptor = interfaceDescriptor;
interfaceList[interfaceIndex].Interface = NULL;
// Move the position to the next interface descriptor
StartPosition = (PUCHAR)interfaceDescriptor + interfaceDescriptor->bLength;
}
// Make sure that the InterfaceDescriptor member of the last element to NULL.
interfaceList[deviceExtension->NumInterfaces].InterfaceDescriptor = NULL;
// Allocate and build an URB for the select-configuration request.
ntStatus = USBD_SelectConfigUrbAllocateAndBuild(
deviceExtension->UsbdHandle,
ConfigurationDescriptor,
interfaceList,
&urb);
if(!NT_SUCCESS(ntStatus))
{
goto Exit;
}
// Allocate the IRP to send the buffer down the USB stack.
// The IRP will be freed by IO manager.
irp = IoAllocateIrp((deviceExtension->NextDeviceObject->StackSize)+1, TRUE);
if (!irp)
{
//Irp could not be allocated.
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
ntStatus = SubmitUrbSync(
deviceExtension->NextDeviceObject,
irp,
urb,
CompletionRoutine);
// Enumerate the pipes in the interface information array, which is now filled with pipe
// information.
for ( interfaceIndex = 0;
interfaceIndex < deviceExtension->NumInterfaces;
interfaceIndex++)
{
ULONG i;
Interface = interfaceList[interfaceIndex].Interface;
for(i=0; i < Interface->NumberOfPipes; i++)
{
pipeHandle = Interface->Pipes[i].PipeHandle;
if (Interface->Pipes[i].PipeType == UsbdPipeTypeInterrupt)
{
deviceExtension->InterruptPipe = pipeHandle;
}
if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN (Interface->Pipes[i].EndpointAddress))
{
deviceExtension->BulkInPipe = pipeHandle;
}
if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT (Interface->Pipes[i].EndpointAddress))
{
deviceExtension->BulkOutPipe = pipeHandle;
}
}
}
Exit:
if(interfaceList)
{
ExFreePool(interfaceList);
interfaceList = NULL;
}
if (urb)
{
USBD_UrbFree( deviceExtension->UsbdHandle, urb);
}
return ntStatus;
}
NTSTATUS CompletionRoutine ( PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context)
{
PKEVENT kevent;
kevent = (PKEVENT) Context;
if (Irp->PendingReturned == TRUE)
{
KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
}
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Select-configuration request completed. \n" ));
return STATUS_MORE_PROCESSING_REQUIRED;
}
Отключение конфигурации для USB-устройства
Чтобы отключить USB-устройство, создайте и отправьте запрос на выборку конфигурации с дескрипторовом конфигурации NULL. Для этого типа запроса можно повторно использовать urb, созданную для запроса, который выбрал конфигурацию на устройстве. Кроме того, можно выделить новую urb, вызвав USBD_UrbAllocate. Перед отправкой запроса необходимо отформатировать URB с помощью макроса UsbBuildSelectConfigurationRequest , как показано в следующем примере кода.
URB Urb;
UsbBuildSelectConfigurationRequest(
&Urb,
sizeof(_URB_SELECT_CONFIGURATION),
NULL
);
Связанные темы
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по