다음을 통해 공유


USB 디바이스에 대한 구성을 선택하는 방법

USB 디바이스에 대한 구성을 선택하려면 디바이스의 클라이언트 드라이버가 지원되는 구성 중 하나 이상을 선택하고 사용할 각 인터페이스의 대체 설정을 지정해야 합니다. 클라이언트 드라이버는 선택 구성 요청에서 이러한 선택을 패키지하고 Microsoft에서 제공하는 USB 드라이버 스택, 특히 USB 버스 드라이버(USB 허브 PDO)로 요청을 보냅니다. USB 버스 드라이버는 지정된 구성에서 각 인터페이스를 선택하고 인터페이스 내의 각 엔드포인트에 대한 통신 채널 또는 파이프를 설정합니다. 요청이 완료되면 클라이언트 드라이버는 선택한 구성에 대한 핸들을 수신하고 각 인터페이스에 대한 활성 대체 설정에 정의된 엔드포인트에 대한 핸들을 파이프합니다. 그런 다음 클라이언트 드라이버는 수신된 핸들을 사용하여 구성 설정을 변경하고 I/O 읽기 및 쓰기 요청을 특정 엔드포인트로 보낼 수 있습니다.

클라이언트 드라이버는 URB_FUNCTION_SELECT_CONFIGURATION 형식의 URB(USB 요청 블록)에서 선택 구성 요청을 보냅니다. 이 항목의 절차에서는 USBD_SelectConfigUrbAllocateAndBuild 루틴을 사용하여 해당 URB를 빌드하는 방법을 설명합니다. 루틴은 URB에 대한 메모리를 할당하고, 선택 구성 요청에 대한 URB의 형식을 지정하고, URB의 주소를 클라이언트 드라이버에 반환합니다.

또는 URB 구조를 할당한 다음 수동으로 또는 UsbBuildSelectConfigurationRequest 매크로를 호출하여 URB 형식을 지정할 수 있습니다.

필수 조건

  • Windows 8부터 USBD_SelectConfigUrbAllocateAndBuild USBD_CreateConfigurationRequestEx 대체합니다.

  • 선택 구성 요청을 보내기 전에 USB 드라이버 스택에 클라이언트 드라이버 등록을 위한 USBD 핸들이 있어야 합니다. USBD 핸들을 만들려면 USBD_CreateHandle 호출합니다.

  • 선택할 구성의 구성 설명자(USB_CONFIGURATION_DESCRIPTOR 구조)를 얻었는지 확인합니다. 일반적으로 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 형식의 URB를 제출하여(_URB_CONTROL_DESCRIPTOR_REQUEST 참조) 디바이스 구성에 대한 정보를 검색합니다. 자세한 내용은 USB 구성 설명자를 참조 하세요.

1단계: USBD_INTERFACE_LIST_ENTRY 구조체 배열 만들기

  1. 구성의 인터페이스 수를 가져옵니다. 이 정보는 USB_CONFIGURATION_DESCRIPTOR 구조체의 bNumInterfaces 멤버에 포함됩니다.

  2. USBD_INTERFACE_LIST_ENTRY 구조체의 배열을 만듭니다. 배열의 요소 수는 인터페이스 수보다 1개 이상이어야 합니다. RtlZeroMemory를 호출 하여 배열을 초기화합니다.

    클라이언트 드라이버는 USBD_INTERFACE_LIST_ENTRY 구조의 배열에서 사용하도록 설정할 각 인터페이스의 대체 설정을 지정합니다.

    • 각 구조체의 InterfaceDescriptor 멤버는 대체 설정을 포함하는 인터페이스 설명자를 가리킵니다.
    • 각 구조체의 인터페이스 멤버는 파이프 멤버의 파이프 정보를 포함하는 USBD_INTERFACE_INFORMATION 구조를 가리킵니. 파이프는 대체 설정에 정의된 각 엔드포인트에 대한 정보를 저장합니다.
  3. 구성에서 각 인터페이스(또는 해당 대체 설정)에 대한 인터페이스 설명자를 가져옵니다. USBD_ParseConfigurationDescriptorEx 호출하여 해당 인터페이스 설명자를 가져올 수 있습니다.

    USB 복합 디바이스의 함수 드라이버 정보:

    USB 디바이스가 복합 디바이스인 경우 Microsoft에서 제공하는 USB 제네릭 부모 드라이버 (Usbccgp.sys)에서 구성을 선택합니다. 복합 디바이스의 함수 드라이버 중 하나인 클라이언트 드라이버는 구성을 변경할 수 없지만 드라이버는 여전히 Usbccgp.sys 통해 선택 구성 요청을 보낼 수 있습니다.

    해당 요청을 보내기 전에 클라이언트 드라이버는 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 요청을 제출해야 합니다. 이에 대한 응답으로 Usbccgp.sys 클라이언트 드라이버가 로드되는 특정 함수와 관련된 인터페이스 설명자 및 기타 설명자만 포함하는 부분 구성 설명 자를 검색합니다. 부분 구성 설명자의 bNumInterfaces 필드에 보고된 인터페이스의 수는 전체 USB 복합 디바이스에 대해 정의된 총 인터페이스 수보다 적습니다. 또한 부분 구성 설명자에서 인터페이스 설명자의 bInterfaceNumber 는 전체 디바이스에 상대적인 실제 인터페이스 번호를 나타냅니다. 예를 들어 Usbccgp.sys 첫 번째 인터페이스에 대해 bNumInterfaces 값이 2이고 bInterfaceNumber 값이 4인 부분 구성 설명자를 보고할 수 있습니다. 인터페이스 번호가 보고된 인터페이스 수보다 큽니다.

    부분 구성에서 인터페이스를 열거하는 동안 인터페이스 수를 기준으로 인터페이스 번호를 계산하여 인터페이스를 검색하지 않도록 합니다. 앞의 예제에서 USBD_ParseConfigurationDescriptorEx 0에서 시작하고, 종료(bNumInterfaces - 1)되고, 각 반복에서 인터페이스 인덱스(InterfaceNumber 매개 변수에 지정됨)를 증가시키는 루프에서 호출되면 루틴이 올바른 인터페이스를 얻지 못합니다. 대신 InterfaceNumber에서 -1을 전달하여 구성 설명자의 모든 인터페이스를 검색해야 합니다. 구현 세부 정보는 이 섹션의 코드 예제를 참조하세요.

    Usbccgp.sys 클라이언트 드라이버에서 보낸 선택 구성 요청을 처리하는 방법에 대한 자세한 내용은 기본이 아닌 USB 구성을 선택하도록 Usbccgp.sys 구성을 참조 하세요.

  4. 배열의 각 요소(마지막 요소 제외)에 대해 InterfaceDescriptor 멤버를 인터페이스 설명자의 주소로 설정합니다. 배열의 첫 번째 요소에 대해 InterfaceDescriptor 멤버를 구성의 첫 번째 인터페이스를 나타내는 인터페이스 설명자의 주소로 설정합니다. 마찬가지로 배열의 n번째 요소에 대해 InterfaceDescriptor 멤버를 구성의 n번째 인터페이스를 나타내는 인터페이스 설명자의 주소로 설정합니다.

  5. 마지막 요소의 InterfaceDescriptor 멤버를 NULL로 설정해야 합니다.

2단계: USB 드라이버 스택에서 할당한 URB에 대한 포인터 가져오기

다음으로 선택할 구성과 채워진 USBD_INTERFACE_LIST_ENTRY 구조 배열을 지정하여 USBD_SelectConfigUrbAllocateAndBuild 호출합니다. 루틴은 다음 작업을 수행합니다.

  • URB를 만들고 지정된 구성, 해당 인터페이스 및 엔드포인트에 대한 정보로 채우고 요청 유형을 URB_FUNCTION_SELECT_CONFIGURATION 설정합니다.

  • 해당 URB 내에서 클라이언트 드라이버가 지정하는 각 인터페이스 설명자에 대한 USBD_INTERFACE_INFORMATION 구조를 할당합니다.

  • 호출자가 제공한 USBD_INTERFACE_LIST_ENTRY 배열의 n번째 요소의 Interface 멤버를 URB의 해당 USBD_INTERFACE_INFORMATION 구조체의 주소로 설정합니다.

  • InterfaceNumber, AlternateSetting, NumberOfPipes, Pipes[i]를 초기화합니다. MaximumTransferSize파이프[i]. PipeFlags 멤버입니다.

    참고 항목

    Windows 7 및 이전 버전에서 클라이언트 드라이버는 USBD_CreateConfigurationRequestEx 호출하여 선택 구성 요청에 대한 URB를 만들었습니다. Windows 2000 USBD_CreateConfigurationRequestEx 파이프[i]를 초기화합니다 . MaximumTransferSize 를 단일 URB 읽기/쓰기 요청에 대한 기본 최대 전송 크기로 설정합니다. 클라이언트 드라이버는 Pipes[i]에서 다른 최대 전송 크기를 지정할 수 있습니다. MaximumTransferSize. USB 스택은 Windows XP, Windows Server 2003 이상 버전의 운영 체제에서 이 값을 무시합니다. MaximumTransferSize에 대한 자세한 내용은 USB 대역폭 할당에서 "USB 전송 및 패킷 크기 설정"을 참조하세요.

3단계: USB 드라이버 스택에 URB 제출

URB를 USB 드라이버 스택에 제출하려면 클라이언트 드라이버가 IOCTL_INTERNAL_USB_SUBMIT_URB I/O 제어 요청을 보내야 합니다. URB 제출에 대한 자세한 내용은 URB 제출 방법을 참조하세요.

URB를 받은 후 USB 드라이버 스택은 각 USBD_INTERFACE_INFORMATION 구조체의 나머지 멤버를 채웁니다. 특히 파이프 배열 멤버는 인터페이스의 엔드포인트와 연결된 파이프에 대한 정보로 채워집니다.

4단계: 요청 완료 시 USBD_INTERFACE_INFORMATION 구조 및 URB를 검사합니다.

USB 드라이버 스택이 요청에 대한 IRP를 완료한 후 스택은 대체 설정 목록과 USBD_INTERFACE_LIST_ENTRY 배열의 관련 인터페이스를 반환합니다.

  1. USBD_INTERFACE_INFORMATION 구조체의 파이프 멤버는 해당 특정 인터페이스의 각 엔드포인트와 연결된 파이프에 대한 정보를 포함하는 USBD_PIPE_INFORMATION 구조의 배열을 가리킵니다. 클라이언트 드라이버는 파이프[i]에서 파이프 핸들을 가져올 수 있습니다. PipeHandle 을 사용하여 특정 파이프에 I/O 요청을 보냅니다. 파이프[i]입니다. PipeType 멤버는 해당 파이프에서 지원하는 엔드포인트 및 전송의 형식을 지정합니다.

  2. URB의 UrbSelectConfiguration 멤버 내에서 USB 드라이버 스택은 URB_FUNCTION_SELECT_INTERFACE 형식의 다른 URB(선택 인터페이스 요청)를 제출하여 대체 인터페이스 설정을 선택하는 데 사용할 수 있는 핸들을 반환합니다. 해당 요청에 대한 URB 구조를 할당하고 빌드하려면 USBD_SelectInterfaceUrbAllocateAndBuild 호출합니다.

    활성화된 인터페이스 내에서 등시, 제어 및 인터럽트 엔드포인트를 지원하기에 대역폭이 부족한 경우 선택 구성 요청 및 선택 인터페이스 요청이 실패할 수 있습니다. 이 경우 USB 버스 드라이버는 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를 다시 사용할 수 있습니다. 또는 USBD_UrbAllocate 호출하여 새 URB를 할당할 수 있습니다. 요청을 제출하기 전에 다음 예제 코드와 같이 UsbBuildSelectConfigurationRequest 매크로를 사용하여 URB의 형식을 지정해야 합니다.

URB Urb;
UsbBuildSelectConfigurationRequest(
  &Urb,
  sizeof(_URB_SELECT_CONFIGURATION),
  NULL
);