如何選取 USB 裝置的設定
若要選取 USB 裝置的組態,裝置的用戶端驅動程式必須至少選擇其中一個支援的組態,並指定要使用之每個介面的替代設定。 用戶端驅動程式會將這些選項封裝在選取組態要求中,並將要求傳送至 Microsoft 提供的 USB 驅動程式堆棧,特別是 USB 總線驅動程式(USB 中樞 PDO)。 USB 總線驅動程式會選取指定組態中的每個介面,並將通道或 管道設定至介面內的每個端點。 要求完成之後,客戶端驅動程式會收到所選組態的句柄,以及每個介面的作用中替代設定中所定義的端點管道句柄。 然後,客戶端驅動程式可以使用收到的句柄來變更組態設定,並將 I/O 讀取和寫入要求傳送至特定端點。
用戶端驅動程式會在類型URB_FUNCTION_SELECT_CONFIGURATION的 USB 要求區塊 (URB) 中傳送選取組態要求。 本主題中的程式描述如何使用 USBD_SelectConfigUrbAllocateAndBuild 例程來建置該 URB。 例程會配置 URB 的記憶體、格式化選取組態要求的 URB,並將 URB 的位址傳回給用戶端驅動程式。
或者,您可以配置 URB 結構,然後手動格式化 URB,或呼叫 UsbBuildSelectConfigurationRequest 宏。
必要條件
從 Windows 8 開始,USBD_SelectConfigUrbAllocateAndBuild會取代USBD_CreateConfigurationRequestEx。
傳送選取組態要求之前,您必須具有 USBD 句柄,才能使用 USB 驅動程式堆疊註冊用戶端驅動程式。 若要建立 USBD 句柄呼叫 USBD_CreateHandle。
請確定您已取得要選取之組態的組態描述元(USB_CONFIGURATION_DESCRIPTOR 結構)。 一般而言,您會提交類型為 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE的 URB(請參閱 _URB_CONTROL_DESCRIPTOR_REQUEST),以擷取裝置設定的相關信息。 如需詳細資訊,請參閱 USB組態描述元。
步驟 1:建立USBD_INTERFACE_LIST_ENTRY結構的陣列
取得組態中的介面數目。 此資訊包含在 USB_CONFIGURATION_DESCRIPTOR 結構的 bNumInterfaces 成員中。
建立USBD_INTERFACE_LIST_ENTRY結構的陣列。 陣列中的元素數目必須大於介面數目。 呼叫 RtlZeroMemory 來初始化陣列。
用戶端驅動程式會指定每個介面中的替代設定,以在USBD_INTERFACE_LIST_ENTRY結構的陣列中啟用。
- 每個結構的 InterfaceDescriptor 成員會指向包含替代設定的介面描述元。
- 每個結構的 Interface 成員會指向在其 Pipes 成員中包含管道資訊的USBD_INTERFACE_INFORMATION結構。 管道會儲存替代設定中定義之每個端點的相關信息。
取得組態中每個介面的介面描述元(或其替代設定)。 您可以呼叫 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,其開頭為零、結尾
(bNumInterfaces - 1)
為 ,並在每次反覆專案中遞增介面索引(在 InterfaceNumber 參數中指定),例程就無法取得正確的介面。 相反地,請務必在 InterfaceNumber 中傳遞 -1,以搜尋組態描述元中的所有介面。 如需實作詳細數據,請參閱本節中的程式代碼範例。如需如何Usbccgp.sys處理用戶端驅動程式所傳送之選取組態要求的資訊,請參閱 設定Usbccgp.sys以選取非預設 USB 組態。
針對陣列中的每個元素(除了最後一個專案之外),請將 InterfaceDescriptor 成員設定為介面描述元的位址。 針對陣列中的第一個專案,將 InterfaceDescriptor 成員設定為表示組態中第一個介面之介面描述元的位址。 同樣地, 針對陣列中的 n個元素,將 InterfaceDescriptor 成員設定為介面描述元的位址,代表 組態中的第 n個介面。
最後 一個專案的 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 和 Pipes[i]。PipeFlags 成員。
注意
在 Windows 7 和更早版本中,用戶端驅動程式會呼叫 USBD_CreateConfigurationRequestEx,為選取組態要求建立 URB。 在 Windows 2000 USBD_CreateConfigurationRequestEx 初始化 Pipes[i]。MaximumTransferSize 為單一 URB 讀取/寫入要求的默認傳輸大小上限。 用戶端驅動程式可以在 Pipes[i] 中 指定不同的傳輸大小上限。MaximumTransferSize。 USB 堆疊會在 Windows XP、Windows Server 2003 和更新版本的作業系統中忽略此值。 如需 MaximumTransferSize的詳細資訊,請參閱 USB 頻寬配置中的<設定 USB 傳輸和封包大小>。
步驟 3:將 URB 提交至 USB 驅動程式堆疊
若要將 URB 提交至 USB 驅動程式堆疊,用戶端驅動程式必須傳送 IOCTL_INTERNAL_USB_SUBMIT_URB I/O 控制要求。 如需提交 URB 的相關信息,請參閱 如何提交 URB。
收到 URB 之後,USB 驅動程式堆疊會填滿每個 USBD_INTERFACE_INFORMATION 結構的其餘成員。 特別是, Pipes 陣列成員會填入與介面端點相關聯的管道相關信息。
步驟 4:在要求完成時,檢查USBD_INTERFACE_INFORMATION結構和 URB
在USB驅動程式堆疊完成要求的 IRP 之後,堆疊會傳回替代設定清單和USBD_INTERFACE_LIST_ENTRY陣列中的相關介面。
每個USBD_INTERFACE_INFORMATION結構的 Pipes 成員會指向一個USBD_PIPE_INFORMATION結構的數位,其中包含與該特定介面之每個端點相關聯的管道相關信息。 用戶端驅動程式可以從 Pipes[i] 取得管道控點 。PipeHandle 並使用它們將 I/O 要求傳送至特定管道。 Pipes[i]。PipeType 成員會指定該管道所支援的端點和傳輸類型。
在 URB 的 UrbSelectConfiguration 成員內,USB 驅動程式堆棧會傳回句柄,您可以藉由提交另一個類型 URB URB_FUNCTION_SELECT_INTERFACE (select-interface request) 來選取替代介面設定。 若要配置並建置該要求的 URB 結構,請呼叫 USBD_SelectInterfaceUrbAllocateAndBuild。
如果沒有足夠的頻寬支援已啟用介面內的不時時、控制及中斷端點,則 select-configuration 要求和 select-interface 要求可能會失敗。 在此情況下,USB 總線驅動程式會將URB標頭的 Status 成員設定為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
);