windows USB 设备驱动开发-USB复合设备的注册
  TEZNKK3IfmPf 9天前 18 0

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,  
        &registerInfo);  

    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 = &registerInfo;  

    //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;  
}
取消注册复合设备
  1. 通过调用 IoAllocateIrp 分配 IRP,并通过调用 IoGetNextIrpStackLocation 获取指向 IRP 的第一个堆栈位置 (IO_STACK_LOCATION) 的指针;
  2. 通过将 IO_STACK_LOCATION 的 Parameters.DeviceIoControl.IoControlCode 成员设置为IOCTL_INTERNAL_USB_UNREGISTER_COMPOSITE_DEVICE来生成请求;
  3. 调用 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;  
}

在后续会讲述复合设备的电源管理问题。 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 9天前 0

暂无评论

推荐阅读
TEZNKK3IfmPf