如何註冊複合裝置
本文說明稱為組合驅動程式的 USB 多功能裝置驅動程式,如何在基礎 USB 驅動程式堆疊中註冊和取消註冊組合裝置。 Windows 會載入Microsoft提供的驅動程式,Usbccgp.sys做為預設復合驅動程式。 本文中的程序適用於取代 Usbccgp.sys 的自訂 Windows 驅動程式模型(WDM)複合式驅動程式。
通用序列總線 (USB) 裝置可以提供多個同時作用中的功能。 這類多功能裝置也稱為複合裝置。 例如,複合裝置可能會定義鍵盤的功能,以及滑鼠的另一個功能。 複合驅動程式會列舉裝置的功能。 複合驅動程式可以在整合型模型中管理這些函式本身,或為每個函式建立實體裝置物件 (PDO)。 USB 函式驅動程式,例如鍵盤驅動程式和滑鼠驅動程式,會管理各自的個別 PDO。
USB 3.0 規格會定義函式暫停和遠端喚醒功能,讓個別函式進入和結束低功率狀態,而不會影響其他函式或整個裝置的電源狀態。 如需此功能的詳細資訊,請參閱 如何在複合驅動程式中實作函式暫止。
若要使用此功能,複合驅動程序必須向基礎 USB 驅動程式堆疊註冊裝置。 由於此功能適用於 USB 3.0 裝置,因此複合驅動程式必須確定基礎堆疊支援版本USBD_INTERFACE_VERSION_602。 透過註冊請求,複合驅動程式:
- 通知基礎 USB 驅動程式堆疊,驅動程式負責傳送要求以啟用遠端喚醒功能。 USB 驅動程式堆疊會處理遠端喚醒要求,以將必要的通訊協定要求傳送至裝置。
- 取得 USB 驅動程式堆疊指派的函式句柄清單(每個函式一個)。 複合驅動程式接著可以在其要求中使用函式句柄,來喚醒與該句柄相關聯的函式以遠端啟動。
復合驅動程式通常會在驅動程式的 AddDevice 或 start-device 例程中傳送註冊要求,以處理 IRP_MN_START_DEVICE。 因此,複合驅動程式會釋放註冊於驅動程式卸載例程中的資源,例如「停止裝置」(IRP_MN_STOP_DEVICE)或「移除裝置」例程(IRP_MN_REMOVE_DEVICE)。
必要條件
傳送註冊要求之前,請確定:
- 您可以使用裝置中的功能數量。 該數字可以從 get-configuration 請求中提取的描述項衍生出來。
- 您在先前呼叫 USBD_CreateHandle 中取得 USBD 控制代碼。
- 基礎 USB 驅動程式堆疊支援 USB 3.0 裝置。 若要這樣做,請呼叫 USBD_IsInterfaceVersionSupported ,並傳遞USBD_INTERFACE_VERSION_602作為要檢查的版本。
如需程式代碼範例,請參閱 如何在複合驅動程式中實作函式暫止。
註冊複合裝置
下列程式說明您應該如何建置和傳送註冊要求,以將複合驅動程式與 USB 驅動程式堆疊產生關聯。
配置COMPOSITE_DEVICE_CAPABILITIES結構,並藉由呼叫COMPOSITE_DEVICE_CAPABILITIES_INIT巨集加以初始化。
將 COMPOSITE_DEVICE_CAPABILITIES 的 CapabilityFunctionSuspend 成員設定為 1。
分配一個 REGISTER_COMPOSITE_DEVICE 結構,然後呼叫 USBD_BuildRegisterCompositeDevice 例程來初始化該結構。 在呼叫中,指定USBD句柄、初始化 COMPOSITE_DEVICE_CAPABILITIES 結構,以及函式數目。
呼叫 IoAllocateIrp 以設定 I/O 要求封包(IRP),並藉由呼叫 IoGetNextIrpStackLocation 來取得 IRP 第一個堆棧位置 (IO_STACK_LOCATION) 的指標。
為能夠容納函式句柄(USBD_FUNCTION_HANDLE)陣列的緩衝區分配記憶體。 陣列中的元素數目必須是 PDO 的數目。
藉由設定下列IO_STACK_LOCATION成員來建置要求:
- 將 Parameters.DeviceIoControl.IoControlCode 設定為 IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE ,以指定要求的類型。
- 將 Parameters.Others.Argument1 設定為已初始化的 REGISTER_COMPOSITE_DEVICE 結構的位址,以指定輸入參數。
- 將 AssociatedIrp.SystemBuffer 設定為步驟 5 中所配置的緩衝區,以指定輸出參數。
呼叫 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傳遞至下一個堆疊位置,以傳送要求。
IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE 請求是由複合裝置驅動程式在 remove-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;
}