USB 多功能設備的驅動程序(稱為復合驅動程序)可以向基礎 USB 驅動程序堆棧注冊和注銷復合設備。 Microsoft 提供的驅動程序(Usbccgp.sys)是由 Windows 加載的默認復合驅動程序。 本文中的過程適用于替換Usbccgp.sys的基于 WDM的自定義 Windows 驅動程序模型 。
通用串行總線 (USB) 設備可以提供多個同時處于活動狀態的功能。 此類多功能設備也稱為 復合設備。 例如,復合設備可能為鍵盤功能定義一個函數,為鼠標定義另一個函數。 復合驅動程序枚舉設備的功能。 復合驅動程序可以在整體模型中管理這些函數本身,或為每個函數創建物理設備對象 (PDO) 。 這些單獨的 PDO 由各自的 USB 功能驅動程序、鍵盤驅動程序和鼠標驅動程序管理。
USB 3.0 規范定義了 函數暫停和遠程喚醒功能 ,使各個函數能夠進入和退出低功耗狀態,而不會影響其他功能或整個設備的電源狀態。?
若要使用該功能,復合驅動程序需要將設備注冊到基礎 USB 驅動程序堆棧。 由于該功能適用于 USB 3.0 設備,因此復合驅動程序必須確保基礎堆棧支持版本USBD_INTERFACE_VERSION_602。 通過注冊請求,復合驅動程序:
- 通知基礎 USB 驅動程序堆棧,驅動程序負責發送請求,以支持功能驅動進行遠程喚醒。 遠程喚醒請求由 USB 驅動程序堆棧處理,該堆棧將必要的協議請求發送到設備;
- 獲取 (USB 驅動程序堆棧分配的每個函數) 一個函數句柄的列表。 然后,復合驅動程序可以在驅動程序的請求中使用函數句柄,以便遠程喚醒與句柄關聯的函數;
通常,復合驅動程序在驅動程序的 AddDevice 或啟動設備例程中發送注冊請求來處理 IRP_MN_START_DEVICE。 因此,復合驅動程序會釋放在驅動程序的卸載例程,例如 stop-device (IRP_MN_STOP_DEVICE ) 或 remove-device 例程 (IRP_MN_REMOVE_DEVICE) 中為釋放在注冊時分配的資源。
先決條件
發送注冊請求之前,請確保:
- 你擁有設備中的函數數。 該數字可以派生 get-configuration 請求檢索到的描述符;
- 在對 USBD_CreateHandle 的上一次調用中,你已獲得 USBD 句柄;
- 基礎 USB 驅動程序堆棧支持 USB 3.0 設備。 為此,請調用 USBD_IsInterfaceVersionSupported 并將 USBD_INTERFACE_VERSION_602 作為版本傳遞給 檢查;
注冊復合設備
以下過程介紹如何生成和發送注冊請求,以將復合驅動程序與 USB 驅動程序堆棧相關聯。
1.分配 COMPOSITE_DEVICE_CAPABILITIES 結構并通過調用 COMPOSITE_DEVICE_CAPABILITIES_INIT 宏對其進行初始化;
2.將 COMPOSITE_DEVICE_CAPABILITIES 的 CapabilityFunctionSuspend 成員設置為 1;
3.分配 REGISTER_COMPOSITE_DEVICE 結構,并通過調用 USBD_BuildRegisterCompositeDevice 例程初始化 結構 。 在調用中,指定 USBD 句柄、初始化 COMPOSITE_DEVICE_CAPABILITIES 結構和函數數;
4.通過調用 IoAllocateIrp (IRP) 分配 I/O 請求數據包,并通過調用 IoGetNextIrpStackLocation 獲取指向 IRP 的第一個堆棧位置的指針 (IO_STACK_LOCATION) ;
5.為足以容納函數句柄數組的緩沖區分配內存 (USBD_FUNCTION_HANDLE) 。 數組中的元素數必須是 PDO 的數量;
6.通過設置 IO_STACK_LOCATION的以下成員來生成請求:
- 通過將 Parameters.DeviceIoControl.IoControlCode 設置為 IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE,指定請求的類型。
- 通過將 Parameters.Others.Argument1 設置為初始化的 REGISTER_COMPOSITE_DEVICE 結構的地址來指定輸入參數。
- 通過將 AssociatedIrp.SystemBuffer 設置為在步驟 5 中分配的緩沖區來指定輸出參數。
7.調用 IoCallDriver ,通過將 IRP 傳遞到下一個堆棧位置來發送請求;
完成后,檢查 USB 驅動程序堆棧返回的函數句柄數組。 可以將數組存儲在驅動程序的設備上下文中,以供將來使用;
下面的代碼示例演示如何生成和發送注冊請求。 該示例假定復合驅動程序將以前獲取的函數數和 USBD 句柄存儲在驅動程序的設備上下文中。
VOID RegisterCompositeDriver(PPARENT_FDO_EXT parentFdoExt)
{ PIRP irp; REGISTER_COMPOSITE_DRIVER registerInfo; COMPOSITE_DRIVER_CAPABILITIES capabilities; NTSTATUS status; PVOID buffer; ULONG bufSize; PIO_STACK_LOCATION nextSp; buffer = NULL; COMPOSITE_DRIVER_CAPABILITIES_INIT(&capabilities); capabilities.CapabilityFunctionSuspend = 1; USBD_BuildRegisterCompositeDriver(parentFdoExt->usbdHandle, capabilities, parentFdoExt->numFunctions, ®isterInfo); irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE); if (irp == NULL) { //IoAllocateIrp failed.status = STATUS_INSUFFICIENT_RESOURCES; goto ExitRegisterCompositeDriver; } nextSp = IoGetNextIrpStackLocation(irp); bufSize = parentFdoExt->numFunctions * sizeof(USBD_FUNCTION_HANDLE); buffer = ExAllocatePoolWithTag (NonPagedPool, bufSize, POOL_TAG); if (buffer == NULL) { // Memory alloc for function-handles failed.status = STATUS_INSUFFICIENT_RESOURCES; goto ExitRegisterCompositeDriver; } nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DRIVER; //Set the input buffer in Argument1 nextSp->Parameters.Others.Argument1 = ®isterInfo; //Set the output buffer in SystemBuffer field for USBD_FUNCTION_HANDLE. irp->AssociatedIrp.SystemBuffer = buffer; // Pass the IRP down to the next device object in the stack. Not shown.status = CallNextDriverSync(parentFdoExt, irp, FALSE); if (!NT_SUCCESS(status)){ //Failed to register the composite driver.goto ExitRegisterCompositeDriver; } parentFdoExt->compositeDriverRegistered = TRUE; parentFdoExt->functionHandleArray = (PUSBD_FUNCTION_HANDLE) buffer; End: if (!NT_SUCCESS(status)) { if (buffer != NULL) { ExFreePoolWithTag (buffer, POOL_TAG); buffer = NULL; } } if (irp != NULL) { IoFreeIrp(irp); irp = NULL; } return;
}
取消注冊復合設備
- 通過調用 IoAllocateIrp 分配 IRP,并通過調用 IoGetNextIrpStackLocation 獲取指向 IRP 的第一個堆棧位置 (IO_STACK_LOCATION) 的指針;
- 通過將 IO_STACK_LOCATION 的 Parameters.DeviceIoControl.IoControlCode 成員設置為IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE來生成請求;
- 調用 IoCallDriver ,通過將 IRP 傳遞到下一個堆棧位置來發送請求;
復合驅動程序在 remove-device 例程的上下文中發送一次 IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE 請求。 請求的目的是刪除 USB 驅動程序堆棧與復合驅動程序及其枚舉函數之間的關聯。 該請求還會清理為維護該關聯而創建的所有資源,以及上一個注冊請求中返回的所有函數句柄。
下面的代碼示例演示如何生成并發送取消注冊復合設備的請求。 該示例假定復合驅動程序以前是通過注冊請求注冊的,如本主題前面所述。
VOID UnregisterCompositeDriver( PPARENT_FDO_EXT parentFdoExt )
{ PIRP irp; PIO_STACK_LOCATION nextSp; NTSTATUS status; PAGED_CODE(); irp = IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE); if (irp == NULL) { //IoAllocateIrp failed.status = STATUS_INSUFFICIENT_RESOURCES; return; } nextSp = IoGetNextIrpStackLocation(irp); nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DRIVER; // Pass the IRP down to the next device object in the stack. Not shown.status = CallNextDriverSync(parentFdoExt, irp, FALSE); if (NT_SUCCESS(status)) { parentFdoExt->compositeDriverRegistered = FALSE; } IoFreeIrp(irp); return;
}
在后續會講述復合設備的電源管理問題。?