如何在串流云桌面中实现外设的远程控制输入——HID人机接口设备驱动简介(键鼠、手写板等)

文章目录

  • 虚拟HID设备
    • 1. HID架构
    • 2. HID接口函数
      • 2.1 Device Discovery and Setup
      • 2.2 Data Movement
      • 2.3 Report Creation and Interpretation
    • 3. 虚拟设备
      • 3.1 HidRegisterMinidriver
      • 3.2 IRP_MJ_INTERNAL_DEVICE_CONTROL
      • 3.3 效果

虚拟HID设备

对于HID设备的虚拟化,主要包括:

  1. 虚拟化鼠标。
  2. 虚拟化键盘。
  3. 虚拟化触摸板(手写笔)。

本文我们来探讨上述HID设备虚拟化的基本实现原理(关于HID设备的虚拟化,需要了解HID描述符相关基础知识,可以参见另外一篇文章:关于HID描述符)。

1. HID架构

在Windows下面HID的基本架构如下:
如何在串流云桌面中实现外设的远程控制输入——HID人机接口设备驱动简介(键鼠、手写板等)_第1张图片

HID Clients包括有驱动,服务以及应用程序,他们都和HIDClass.sys进行通信;通常来说每一次通信的对象都是一个指定的设备(例如键盘,鼠标等)。通常通过hardware ID或者HID Collection来标识一个设备,通过通信遵循如下规则:

  • 用户模式驱动或者应用程序通过HIDClass提供的HidD_xxx来获取HID Collection。
  • 内核驱动或者用户驱动以及应用程序通过HID分析支持程序HidP_xxx,内核驱动程序使用HID 类驱动的IOCTL来处理HID报表。
Mode Drivers Applications
User Mode HidD_Xxx HidP_Xxx
Kernel Mode HidD_Xxx
IOCTL_HID_xxx
N/A

HID Transport是具体的硬件设备,我们HID Transport向上提供硬件相关信息,HID Client从来不主动和HID Transport直接通信,都是通过HIDClass.sys来进行中转的,这样给我们HID Client和HID Transport的开发都带来了非常大的便利;这也是Windows MiniPort驱动实现的基本框架。

对于HID的设备的虚拟,我们主要是需要实现HID Transport。

2. HID接口函数

Windows提供了如下的HID函数接口来或者和操作HID设备:

  1. Device Discovery and Setup。
  2. Data Movement。
  3. Report Creation and Interpretation。

2.1 Device Discovery and Setup

API 描述
HidD_GetAttributes 请求获得HID设备的厂商ID、产品ID和版本号
HidD_GetHidGuid 请求获得HID设备的GUID
HidD_GetIndexString 请求获得由索引识别的字符串
HidD_GetManufactureString 请求获得设备制造商字符串
HidD_GetPhysicalDescriptor 请求获得设备实体字符串
HidD_GetPreparsedData 请求获得与设备能力信息相关的缓冲区的代号
HidD_GetProductString 请求获得产品字符串
HidD_GetSerialNumberString 请求获得产品序列号字符串
HidD_GetNumInputBuffer 获得驱动程序用于存储输入报表的环形缓冲区的大小,默认值是8
HidD_SetNumInputBuffer 设置驱动程序用于存储输入报表的环形缓冲区的大小

2.2 Data Movement

API 描述
HidD_GetInputReport 从设备读取一个特征报表
HidD_SetFeature 向设备传送一个特征报表
HidD_SetOutputReport 向设备传送输出报表
WriteFile 向设备传送输出报表
ReadFile 从设备读取输入报表

2.3 Report Creation and Interpretation

API 描述
HidP_GetButtonCaps 请求获得HID报表中所有按钮的能力
HidP_GetButtons 从设备读取包含每个按下的按钮的用法(Usage)的缓冲区的指针,该请求可以设定一个Usage Page
HidP_GetButtonEx 从设备读取包含每个按下的按钮的Usage和Usage Page的缓冲区的指针
HidP_GetCaps 请求获得用于描述设备能力的结构的指针
HidP_GetLinkCollectionNotes 请求获得描述在顶层集合中的连接集合(Link Collection)关系的结构的数组
HidP_GetSpecificButtonCaps 请求获得报表中按钮的能力,该请求可以设定一个Usage Page、Usage或是Link Collection
HidP_GetSpecificValueCaps 请求获得报表中数值的能力,该请求可以设定一个Usage Page、Usage或是Link Collection
HidP_GetValueCaps 请求获得 HID 报表中所有数值的能力
HidP_MaxUsageListLength 请求获得 HID 报表中可以回传的按钮的最大数目,该请求可以设定一个Usage Page
HidP_UsageListDifference 比较两个按钮列表,并且求出在一个列表中设定而在另一个列表中没有设定的按钮
HidP_GetScaledUsageValue 从设备读取一个已经经过比例因子调整的有符号数值
HidP_GetUsageValue 从设备读取一个指向数值的指针
HidP_GetUsageValueArray 从设备读取包含多个数据项的Usage的数据
HidP_SetButtons 向设备传送设置按钮的数据
HidP_SetScaledUsageValue 将一个实际数值转换成设备使用的逻辑数值,并将其插入到报表中
HidP_SetUsageValue 向设备传送数据
HidP_SetUsageValueArray 向设备传送包含多个数据项的Usage的数据

3. 虚拟设备

3.1 HidRegisterMinidriver

对于一个HID的Miniport硬件驱动来说,都是通过HidRegisterMinidriver函数来完成注册的,这个函数是HIDClass.sys提供,也就是说HIDClass.sys提供了框架,给我们MiniPort驱动的开发。

对于HidRegisterMinidriver工作大致可以总结为如下:

  1. IoAllocateDriverObjectExtension创建驱动的上下文HIDCLASS_DRIVER_EXTENSION
  2. 保存MiniPort驱动的相关信息到HIDCLASS_DRIVER_EXTENSION结构中,如下:
RtlCopyMemory(hidDriverExtension->MajorFunction,
    minidriverObject->MajorFunction,sizeof( PDRIVER_DISPATCH ) * (IRP_MJ_MAXIMUM_FUNCTION + 1) );

minidriverObject->DriverUnload = HidpDriverUnload;

hidDriverExtension->AddDevice = driverExtension->AddDevice;

3.2 IRP_MJ_INTERNAL_DEVICE_CONTROL

对于HID的虚拟硬件设备,对上层实现的接口都是通过IRP_MJ_INTERNAL_DEVICE_CONTROL来提供的,HID需要实现的相关IOCTL如下:

//
// Internal IOCTLs for the class/mini driver interface.
//

#define IOCTL_HID_GET_DEVICE_DESCRIPTOR             HID_CTL_CODE(0)
#define IOCTL_HID_GET_REPORT_DESCRIPTOR             HID_CTL_CODE(1)
#define IOCTL_HID_READ_REPORT                       HID_CTL_CODE(2)
#define IOCTL_HID_WRITE_REPORT                      HID_CTL_CODE(3)
#define IOCTL_HID_GET_STRING                        HID_CTL_CODE(4)
#define IOCTL_HID_ACTIVATE_DEVICE                   HID_CTL_CODE(7)
#define IOCTL_HID_DEACTIVATE_DEVICE                 HID_CTL_CODE(8)
#define IOCTL_HID_GET_DEVICE_ATTRIBUTES             HID_CTL_CODE(9)
#define IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST    HID_CTL_CODE(10)

我们知道HID最重要的两个描述符是:

  1. HID设备描述符。HID设备描述符通过IOCTL_HID_GET_DEVICE_DESCRIPTOR向上层提供。
  2. HID报表描述符。HID报表描述符通过IOCTL_HID_GET_REPORT_DESCRIPTOR向上层提供。

其他操作包括IOCTL_HID_READ_REPORT实现用来读取报表描述符,IOCTL_HID_WRITE_REPORT用来写入报表描述符。

这里我们需要定义两个描述符,例如可以定义如下:

HID_REPORT_DESCRIPTOR           DefaultReportDescriptor[] = {
    0x06,0x00, 0xFF,                // USAGE_PAGE (Vender Defined Usage Page)     
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)      
    0xA1,0x01,                           // COLLECTION (Application)        
    0x85,CONTROL_FEATURE_REPORT_ID,           // REPORT_ID (1)                      
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)              
    0x15,0x00,                          // LOGICAL_MINIMUM(0)                   
    0x26,0xff, 0x00,                // LOGICAL_MAXIMUM(255)               
    0x75,0x08,                          // REPORT_SIZE (0x08)                     
    0x95,0x01,                          // REPORT_COUNT (0x01)                    
    0xB1,0x00,                          // FEATURE (Data,Ary,Abs)
    0x09,0x01,                          // USAGE (Vendor Usage 0x01)              
    0x75,0x08,                          // REPORT_SIZE (0x08)                     
    0x95,INPUT_REPORT_BYTES,           // REPORT_COUNT (0x01)                    
    0x81,0x00,                      // INPUT (Data,Ary,Abs)
    0xC0                                // END_COLLECTION                       

};

HID_DESCRIPTOR              DefaultHidDescriptor = {
    0x09,   // length of HID descriptor
    0x21,   // descriptor type == HID  0x21
    0x0100, // hid spec release
    0x00,   // country code == Not Specified
    0x01,   // number of HID class descriptors
    { 0x22,   // report descriptor type 0x22
    sizeof(DefaultReportDescriptor) }  // total length of report descriptor
};

3.3 效果

通过实现相关的HID硬件特性之后,我们可以虚拟化如下HID设备:

如何在串流云桌面中实现外设的远程控制输入——HID人机接口设备驱动简介(键鼠、手写板等)_第2张图片

并且我们可以直接操作虚拟化的驱动来模拟各种HID设备信息,如下我们利用虚拟HID键盘向系统开始菜单输入一个Hello:
如何在串流云桌面中实现外设的远程控制输入——HID人机接口设备驱动简介(键鼠、手写板等)_第3张图片

你可能感兴趣的:(虚拟化技术,嵌入式硬件,驱动开发,c++,云计算)