1# 开发适用HID协议的设备驱动 2<!--Kit: Driver Development Kit--> 3<!--Subsystem: Driver--> 4<!--Owner: @lixinsheng2--> 5<!--Designer: @w00373942--> 6<!--Tester: @dong-dongzhen--> 7<!--Adviser: @w_Machine_cc--> 8 9## 简介 10 11HID DDK(HID Driver Develop Kit)是为开发者提供的HID设备驱动程序开发套件,支持开发者基于用户态,在应用层开发HID设备驱动。提供了一系列主机侧访问设备的接口,包括创建设备、向设备发送事件、销毁设备、打开关闭设备、读取写入报告、获取设备信息等。 12 13凡是采用USB总线,通过HID协议传输数据的设备,或者通过扩展外设驱动创建虚拟设备,来实现与非标设备的信息交互都可以使用HID DDK开发设备驱动。 14 15### 基本概念 16 17在进行HID DDK开发前,开发者应了解以下基本概念: 18 19- **HID** 20 21 HID(Human Interface Device),中文意思是“人机接口设备”。它是一类用于实现人与计算机或其他电子设备交互的硬件设备。HID 设备的主要功能是将用户的输入(如按键、点击、移动等)转换为数据信号,并将这些信号发送给主机设备(如计算机、平板、游戏机等),从而实现用户对设备的控制和操作。 22 23- **DDK** 24 25 DDK(Driver Develop Kit)是OpenHarmony基于扩展外设框架,为开发者提供的驱动应用开发的工具包,可针对非标USB串口设备,开发对应的驱动。 26 27### 实现原理 28 29非标外设应用通过扩展外设管理服务获取HID设备的ID,通过RPC将ID和要操作的动作下发给HID设备驱动应用,驱动应用通过调用HID DDK接口可创建、销毁HID设备,以及对HID设备发送事件,获取HID报文,解析报文等,DDK接口使用HDI服务将指令下发至内核驱动,内核驱动使用指令与设备通信。 30 31**图1** HID DDK调用原理 32 33 34 35## 约束与限制 36 37- HID DDK开放API支持非标HID类外设扩展驱动开发场景。 38 39- HID DDK开放API仅允许DriverExtensionAbility生命周期内使用。 40 41- 使用HID DDK开放API需要在module.json5中声明匹配的ACL权限,例如ohos.permission.ACCESS_DDK_HID。 42 43## 接口说明 44 45| 名称 | 描述 | 46| -------- | -------- | 47| OH_Hid_CreateDevice(Hid_Device *hidDevice, Hid_EventProperties *hidEventProperties) | 创建HID设备。请在设备使用完后使用OH_Hid_DestroyDevice销毁设备。 | 48| OH_Hid_EmitEvent(int32_t deviceId, const Hid_EmitItem items[], uint16_t length) | 向指定deviceId的HID设备发送事件。 | 49| OH_Hid_DestroyDevice(int32_t deviceId) | 销毁指定deviceId的HID设备。 | 50| int32_t OH_Hid_Init(void) | 初始化HID DDK。 | 51| int32_t OH_Hid_Release(void) | 释放HID DDK。 | 52| int32_t OH_Hid_Open(uint64_t deviceId, uint8_t interfaceIndex, Hid_DeviceHandle **dev) | 打开deviceId和interfaceIndex指定的设备。 | 53| int32_t OH_Hid_Close(Hid_DeviceHandle **dev) | 关闭设备。 | 54| int32_t OH_Hid_Write(Hid_DeviceHandle *dev, uint8_t *data, uint32_t length, uint32_t *bytesWritten) | 向设备写入报告。 | 55| int32_t OH_Hid_ReadTimeout(Hid_DeviceHandle *dev, uint8_t *data, uint32_t buffSize, int timeout, uint32_t *bytesRead) | 在指定的超时时间内从设备读取报告。 | 56| int32_t OH_Hid_Read(Hid_DeviceHandle *dev, uint8_t *data, uint32_t buffSize, uint32_t *bytesRead) | 从设备读取报告,默认为阻塞模式(阻塞等待直到有数据可读取),可以调用OH_Hid_SetNonBlocking改变模式。 | 57| int32_t OH_Hid_SetNonBlocking(Hid_DeviceHandle *dev, int nonblock) | 设置设备读取模式为非阻塞。 | 58| int32_t OH_Hid_GetRawInfo(Hid_DeviceHandle *dev, Hid_RawDevInfo *rawDevInfo) | 获取设备原始信息。 | 59| int32_t OH_Hid_GetRawName(Hid_DeviceHandle *dev, char *data, uint32_t buffSize) | 获取设备原始名称。 | 60| int32_t OH_Hid_GetPhysicalAddress(Hid_DeviceHandle *dev, char *data, uint32_t buffSize) | 获取设备物理地址。 | 61| int32_t OH_Hid_GetRawUniqueId(Hid_DeviceHandle *dev, uint8_t *data, uint32_t buffSize) | 获取设备原始唯一标识符。 | 62| int32_t OH_Hid_SendReport(Hid_DeviceHandle *dev, Hid_ReportType reportType, const uint8_t *data, uint32_t length) | 向设备发送报告。 | 63| int32_t OH_Hid_GetReport(Hid_DeviceHandle *dev, Hid_ReportType reportType, uint8_t *data, uint32_t buffSize) | 获取设备报告。 | 64| int32_t OH_Hid_GetReportDescriptor(Hid_DeviceHandle *dev, uint8_t *buf, uint32_t buffSize, uint32_t *bytesRead) | 获取设备报告描述符。 | 65 66详细的接口说明请参考[HID DDK](../../reference/apis-driverdevelopment-kit/capi-hidddk.md)。 67 68## 开发步骤 69 70### HID基础驱动能力开发 71 72以下步骤描述了如何使用 **HID DDK**开发HID设备驱动: 73 74**添加动态链接库** 75 76CMakeLists.txt中添加以下lib。 77```txt 78libhid.z.so 79``` 80 81**头文件** 82```c++ 83#include <hid/hid_ddk_api.h> 84#include <hid/hid_ddk_types.h> 85``` 86 871. 创建设备。 88 89 使用 **hid_ddk_api.h** 的 **OH_Hid_CreateDevice** 接口创建HID设备,成功返回设备deviceId,失败返回[错误码](../../reference/apis-driverdevelopment-kit/capi-hid-ddk-types-h.md#hid_ddkerrcode)。 90 91 ```c++ 92 // 构建HID设备属性 93 std::vector<Hid_DeviceProp> deviceProp = {HID_PROP_DIRECT}; //需要引入vector头文件 94 std::string deviceName = "keyboard"; 95 Hid_Device hidDevice = { 96 .deviceName = deviceName.c_str(), 97 .vendorId = 0x6006, 98 .productId = 0x6006, 99 .version = 1, 100 .bustype = 3, 101 .properties = deviceProp.data(), 102 .propLength = (uint16_t)deviceProp.size() 103 }; 104 // 构建HID设备关注的事件属性 105 std::vector<Hid_EventType> eventType = {HID_EV_ABS, HID_EV_KEY, HID_EV_SYN, HID_EV_MSC}; 106 Hid_EventTypeArray eventTypeArray = {.hidEventType = eventType.data(), .length = (uint16_t)eventType.size()}; 107 std::vector<Hid_KeyCode> keyCode = {HID_BTN_TOOL_PEN, HID_BTN_TOOL_RUBBER, HID_BTN_TOUCH, HID_BTN_STYLUS, HID_BTN_RIGHT}; 108 Hid_KeyCodeArray keyCodeArray = {.hidKeyCode = keyCode.data(), .length = (uint16_t)keyCode.size()}; 109 std::vector<Hid_MscEvent> mscEvent = {HID_MSC_SCAN}; 110 Hid_MscEventArray mscEventArray = {.hidMscEvent = mscEvent.data(), .length = (uint16_t)mscEvent.size()}; 111 std::vector<Hid_AbsAxes> absAxes = {HID_ABS_X, HID_ABS_Y, HID_ABS_PRESSURE}; 112 Hid_AbsAxesArray absAxesArray = {.hidAbsAxes = absAxes.data(), .length = (uint16_t)absAxes.size()}; 113 Hid_EventProperties hidEventProp = { 114 .hidEventTypes = eventTypeArray, 115 .hidKeys = keyCodeArray, 116 .hidAbs = absAxesArray, 117 .hidMiscellaneous = mscEventArray 118 }; 119 // 创建设备并获取到deviceId 120 int32_t deviceId = OH_Hid_CreateDevice(&hidDevice, &hidEventProp); 121 ``` 122 1232. 向指定deviceId的HID设备发送事件。 124 125 使用 **hid_ddk_api.h** 的 **OH_Hid_EmitEvent** 向指定的deviceId的设备发送事件。 126 127 ```c++ 128 // 构建需要发送事件 129 Hid_EmitItem event = {.type = HID_EV_MSC, .code = HID_MSC_SCAN, .value = 0x000d0042}; 130 std::vector<Hid_EmitItem> itemVec; 131 itemVec.push_back(event); 132 // 向指定deviceId的HID设备发送事件 133 int32_t ret = OH_Hid_EmitEvent(deviceId, itemVec.data(), (uint16_t)itemVec.size()); 134 ``` 135 1363. 释放资源。 137 138 在所有请求处理完毕,程序退出前,使用 **hid_ddk_api.h** 的 **OH_Hid_DestroyDevice** 接口销毁HID设备。 139 140 ```c++ 141 // 销毁HID设备 142 int32_t ret = OH_Hid_DestroyDevice(deviceId); 143 ``` 144 145### HID报文通信驱动能力开发 146 147以下步骤描述了如何使用 **HID DDK** 开发HID报文通信驱动: 148 149**添加动态链接库** 150 151CMakeLists.txt中添加以下lib。 152```txt 153libhid.z.so 154``` 155 156**头文件** 157```c++ 158#include <hid/hid_ddk_api.h> 159#include <hid/hid_ddk_types.h> 160``` 161 1621. 初始化DDK。 163 164 使用 **hid_ddk_api.h** 的 **OH_Hid_Init** 初始化HID DDK。 165 166 ```c++ 167 // 初始化HID DDK 168 OH_Hid_Init(); 169 ``` 170 1712. 打开设备。 172 173 初始化HID DDK后,使用 **hid_ddk_api.h** 的 **OH_Hid_Open** 打开HID设备。 174 175 ```c++ 176 uint64_t deviceId = 0x100000003; 177 uint8_t interfaceIndex1 = 0; 178 uint8_t interfaceIndex2 = 1; 179 Hid_DeviceHandle *dev = NULL; 180 Hid_DeviceHandle *devFeature = NULL; 181 // 打开deviceId和interfaceIndex1指定的HID设备(一般为/dev/hidraw0设备文件) 182 OH_Hid_Open(deviceId, interfaceIndex1, &dev); 183 // 打开deviceId和interfaceIndex2指定的HID设备(一般为/dev/hidraw1设备文件) 184 OH_Hid_Open(deviceId, interfaceIndex2, &devFeature); 185 ``` 186 1873. 向HID设备写入/发送报告(HID设备与主机之间交换的数据包)。 188 - 当报告类型为HID_OUTPUT_REPORT(输出报告)时,支持如下两种写入/发送方式。 189 - 使用 **hid_ddk_api.h** 的 **OH_Hid_Write** 向HID设备写入一个输出报告。 190 191 ```c++ 192 uint8_t data[] = {0x02, 0x02}; 193 uint32_t bytesWritten = 0; 194 // 写入报告 195 int32_t ret = OH_Hid_Write(dev, data, sizeof(data), &bytesWritten); 196 ``` 197 198 - 使用 **hid_ddk_api.h** 的 **OH_Hid_SendReport** 向HID设备发送一个输出报告。 199 200 ```c++ 201 uint8_t data1[2] = {0x00}; 202 // 指定报告编号 203 data1[0] = 0x02; 204 // 设置报告数据 205 data1[1] = 0x02; 206 207 // 发送输出报告 208 int32_t ret = OH_Hid_SendReport(dev, HID_OUTPUT_REPORT, data1, sizeof(data1)); 209 ``` 210 211 - 当报告类型为HID_FEATURE_REPORT(特性报告)时,使用 **hid_ddk_api.h** 的 **OH_Hid_SendReport** 向HID设备发送一个特性报告。 212 213 ```c++ 214 uint8_t data2[2] = {0x00}; 215 // 指定报告编号 216 data2[0] = 0x02; 217 // 设置报告数据 218 data2[1] = 0x02; 219 220 // 发送特性报告 221 int32_t ret = OH_Hid_SendReport(devFeature, HID_FEATURE_REPORT, data2, sizeof(data2)); 222 ``` 223 2244. 从HID设备读取报告。 225 - 当报告类型为HID_INPUT_REPORT(输入报告)时,支持如下三种读取方式。 226 - 使用 **hid_ddk_api.h** 的 **OH_Hid_Read** 或者 **OH_Hid_ReadTimeout** 以阻塞模式从HID设备读取一个输入报告。 227 228 ```c++ 229 uint8_t data3[9] = {0x00}; 230 uint32_t bytesRead = 0; 231 // 从HID设备读取报告 232 int32_t ret = OH_Hid_Read(dev, data3, sizeof(data3), &bytesRead); 233 234 uint8_t data4[9] = {0x00}; 235 // 在指定的超时时间内从HID设备读取报告 236 ret = OH_Hid_ReadTimeout(dev, data4, sizeof(data4), 10000, &bytesRead); 237 ``` 238 239 - 使用 **hid_ddk_api.h** 的 **OH_Hid_SetNonBlocking** 和 **OH_Hid_Read** 以非阻塞模式从HID设备读取一个输入报告。 240 241 ```c++ 242 // 1启用非阻塞,0禁用非阻塞 243 int32_t ret = OH_Hid_SetNonBlocking(dev, 1); 244 245 // 本示例执行时,设备可能没有数据,因此需要等待用户输入 246 sleep(1); 247 uint8_t data5[9] = {0}; 248 uint32_t bytesRead = 0; 249 // 从HID设备读取报告 250 ret = OH_Hid_Read(dev, data5, sizeof(data5), &bytesRead); 251 ``` 252 253 - 使用 **hid_ddk_api.h** 的 **OH_Hid_GetReport** 从HID设备读取一个输入报告。 254 255 ```c++ 256 uint8_t data6[9] = {0}; 257 // 指定报告编号 258 data6[0] = 0x00; 259 260 // 读取输入报告 261 int32_t ret = OH_Hid_GetReport(dev, HID_INPUT_REPORT, data6, sizeof(data6)); 262 ``` 263 264 - 当报告类型为HID_FEATURE_REPORT(特性报告)时,使用 **hid_ddk_api.h** 的 **OH_Hid_GetReport** 从HID设备读取一个特性报告。 265 266 ```c++ 267 uint8_t data7[8] = {0}; 268 // 指定报告编号 269 data7[0] = 0x07; 270 271 // 读取特性报告 272 int32_t ret = OH_Hid_GetReport(devFeature, HID_FEATURE_REPORT, data7, sizeof(data7)); 273 ``` 274 2755. 获取设备原始信息、原始名称、物理地址、原始唯一标识符。 276 277 使用 **hid_ddk_api.h** 的 **OH_Hid_GetRawInfo** 获取HID设备原始信息,使用 **OH_Hid_GetRawName** 获取HID设备原始名称,使用 **OH_Hid_GetPhysicalAddress** 获取HID设备物理地址,使用 **OH_Hid_GetRawUniqueId** 获取HID设备原始唯一标识符。这些信息可被上层应用引用,例如在界面中展示设备信息等。 278 279 ```c++ 280 struct Hid_RawDevInfo rawDevInfo; 281 int32_t ret = OH_Hid_GetRawInfo(dev, &rawDevInfo); 282 283 char rawName[1024] = {0}; 284 ret = OH_Hid_GetRawName(dev, rawName, sizeof(rawName)); 285 286 char physicalAddress[1024] = {0}; 287 ret = OH_Hid_GetPhysicalAddress(dev, physicalAddress, sizeof(physicalAddress)); 288 289 uint8_t uniqueIdData[64] = {0}; 290 ret = OH_Hid_GetRawUniqueId(dev, uniqueIdData, sizeof(uniqueIdData)); 291 ``` 292 2936. 获取报告描述符。 294 295 使用 **hid_ddk_api.h** 的 **OH_Hid_GetReportDescriptor** 获取HID设备报告描述符。 296 297 ```c++ 298 uint8_t desData[1024] = {0}; 299 uint32_t bytesRead = 0; 300 int32_t ret = OH_Hid_GetReportDescriptor(dev, desData, sizeof(desData), &bytesRead); 301 ``` 302 3037. 关闭设备。 304 305 在所有请求处理完毕后,使用 **hid_ddk_api.h** 的 **OH_Hid_Close** 关闭设备。 306 307 ```c++ 308 // 关闭设备 309 OH_Hid_Close(&dev); 310 OH_Hid_Close(&devFeature); 311 ``` 312 3138. 释放DDK。 314 315 在关闭HID设备后,使用 **hid_ddk_api.h** 的 **OH_Hid_Release** 释放HID DDK。 316 317 ```c++ 318 // 释放HID DDK 319 OH_Hid_Release(); 320 ```