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