[HID_WINETEST] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / modules / rostests / winetests / hid / device.c
1 /*
2 * Copyright (c) 2017 Aric Stewart
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include <stdio.h>
20 #include "ntstatus.h"
21 #define WIN32_NO_STATUS
22 #include "windows.h"
23 #include "setupapi.h"
24 #include "hidusage.h"
25 #include "ddk/hidsdi.h"
26
27 #include "wine/test.h"
28
29 #define READ_MAX_TIME 5000
30
31 typedef void (device_test)(HANDLE device);
32
33 static void test_device_info(HANDLE device)
34 {
35 PHIDP_PREPARSED_DATA ppd;
36 HIDP_CAPS Caps;
37 HIDD_ATTRIBUTES attributes;
38 NTSTATUS status;
39 BOOL rc;
40 WCHAR device_name[128];
41
42 rc = HidD_GetPreparsedData(device, &ppd);
43 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
44 status = HidP_GetCaps(ppd, &Caps);
45 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
46 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
47 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
48 trace("Found device %s (%02x, %02x)\n", wine_dbgstr_w(device_name), Caps.UsagePage, Caps.Usage);
49 rc = HidD_FreePreparsedData(ppd);
50 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
51 rc = HidD_GetAttributes(device, &attributes);
52 ok(rc, "Failed to get device attributes (0x%x)\n", GetLastError());
53 ok(attributes.Size == sizeof(attributes), "Unexpected HIDD_ATTRIBUTES size: %d\n", attributes.Size);
54 trace("Device attributes: vid:%04x pid:%04x ver:%04x\n", attributes.VendorID, attributes.ProductID, attributes.VersionNumber);
55 }
56
57 static void run_for_each_device(device_test *test)
58 {
59 GUID hid_guid;
60 HDEVINFO info_set;
61 DWORD index = 0;
62 SP_DEVICE_INTERFACE_DATA interface_data;
63 DWORD detail_size = MAX_PATH * sizeof(WCHAR);
64 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
65
66 HidD_GetHidGuid(&hid_guid);
67
68 ZeroMemory(&interface_data, sizeof(interface_data));
69 interface_data.cbSize = sizeof(interface_data);
70
71 data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data) + detail_size);
72 data->cbSize = sizeof(*data);
73
74 info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
75 while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
76 {
77 index ++;
78
79 if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
80 {
81 HANDLE file = CreateFileW(data->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
82 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_ACCESS_DENIED)
83 {
84 trace("Not enough permissions to read device %s.\n", wine_dbgstr_w(data->DevicePath));
85 continue;
86 }
87 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
88 {
89 trace("Device is busy: %s.\n", wine_dbgstr_w(data->DevicePath));
90 continue;
91 }
92
93 ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n",
94 wine_dbgstr_w(data->DevicePath), GetLastError());
95
96 if (file != INVALID_HANDLE_VALUE)
97 test(file);
98
99 CloseHandle(file);
100 }
101 }
102 HeapFree(GetProcessHeap(), 0, data);
103 SetupDiDestroyDeviceInfoList(info_set);
104 }
105
106 static HANDLE get_device(USHORT page, USHORT usages[], UINT usage_count, DWORD access)
107 {
108 GUID hid_guid;
109 HDEVINFO info_set;
110 DWORD index = 0;
111 SP_DEVICE_INTERFACE_DATA interface_data;
112 DWORD detail_size = MAX_PATH * sizeof(WCHAR);
113 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
114 NTSTATUS status;
115 BOOL rc;
116
117 HidD_GetHidGuid(&hid_guid);
118
119 ZeroMemory(&interface_data, sizeof(interface_data));
120 interface_data.cbSize = sizeof(interface_data);
121
122 data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
123 data->cbSize = sizeof(*data);
124
125 info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
126 while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
127 {
128 index ++;
129
130 if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
131 {
132 PHIDP_PREPARSED_DATA ppd;
133 HIDP_CAPS Caps;
134 HANDLE file = CreateFileW(data->DevicePath, access, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
135 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_ACCESS_DENIED)
136 {
137 trace("Not enough permissions to read device %s.\n", wine_dbgstr_w(data->DevicePath));
138 continue;
139 }
140 ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError());
141
142 rc = HidD_GetPreparsedData(file, &ppd);
143 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
144 status = HidP_GetCaps(ppd, &Caps);
145 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
146 rc = HidD_FreePreparsedData(ppd);
147 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
148 if (!page || page == Caps.UsagePage)
149 {
150 int j;
151 if (!usage_count)
152 {
153 HeapFree(GetProcessHeap(), 0, data);
154 SetupDiDestroyDeviceInfoList(info_set);
155 return file;
156 }
157 for (j = 0; j < usage_count; j++)
158 if (!usages[j] || usages[j] == Caps.Usage)
159 {
160 HeapFree(GetProcessHeap(), 0, data);
161 SetupDiDestroyDeviceInfoList(info_set);
162 return file;
163 }
164 }
165 CloseHandle(file);
166 }
167 }
168 HeapFree(GetProcessHeap(), 0, data);
169 SetupDiDestroyDeviceInfoList(info_set);
170 return NULL;
171 }
172
173 static void process_data(HIDP_CAPS Caps, PHIDP_PREPARSED_DATA ppd, CHAR *data, DWORD data_length)
174 {
175 INT i;
176 NTSTATUS status;
177
178 if (Caps.NumberInputButtonCaps)
179 {
180 USAGE button_pages[100];
181
182 for (i = 1; i < 0xff; i++)
183 {
184 ULONG usage_length = 100;
185 status = HidP_GetUsages(HidP_Input, i, 0, button_pages, &usage_length, ppd, data, data_length);
186 ok (status == HIDP_STATUS_SUCCESS || usage_length == 0,
187 "HidP_GetUsages failed (%x) but usage length still %i\n", status, usage_length);
188 if (usage_length)
189 {
190 CHAR report[50];
191 int count;
192 int j;
193
194 count = usage_length;
195 j = 0;
196 report[0] = 0;
197 trace("\tButtons [0x%x: %i buttons]:\n", i, usage_length);
198 for (count = 0; count < usage_length; count += 15)
199 {
200 for (j=count; j < count+15 && j < usage_length; j++)
201 {
202 CHAR btn[7];
203 sprintf(btn, "%i ", button_pages[j]);
204 strcat(report, btn);
205 }
206 trace("\t\t%s\n", report);
207 }
208 }
209 }
210 }
211
212 if (Caps.NumberInputValueCaps)
213 {
214 ULONG value;
215 USHORT length;
216 HIDP_VALUE_CAPS *values = NULL;
217
218 values = HeapAlloc(GetProcessHeap(), 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps);
219 length = Caps.NumberInputValueCaps;
220 status = HidP_GetValueCaps(HidP_Input, values, &length, ppd);
221 ok(status == HIDP_STATUS_SUCCESS, "Failed to get value caps (%x)\n",status);
222
223 trace("\tValues:\n");
224 for (i = 0; i < length; i++)
225 {
226 status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
227 values[i].Range.UsageMin, &value, ppd, data, data_length);
228 ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
229 values[i].UsagePage, values[i].Range.UsageMin, status);
230 trace("[%02x, %02x]: %u\n",values[i].UsagePage, values[i].Range.UsageMin, value);
231 }
232
233 HeapFree(GetProcessHeap(), 0, values);
234 }
235 }
236
237 static void test_read_device(void)
238 {
239 PHIDP_PREPARSED_DATA ppd;
240 HIDP_CAPS Caps;
241 OVERLAPPED overlapped;
242 WCHAR device_name[128];
243 CHAR *data = NULL;
244 DWORD read;
245 BOOL rc;
246 NTSTATUS status;
247 DWORD timeout, tick, spent, max_time;
248 char *report;
249
250 USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
251 HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
252
253 if (!device)
254 device = get_device(0x0, NULL, 0x0, GENERIC_READ);
255
256 if (!device)
257 {
258 trace("No device found for reading\n");
259 return;
260 }
261 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
262 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
263 trace("Read tests on device :%s\n",wine_dbgstr_w(device_name));
264
265 rc = HidD_GetPreparsedData(device, &ppd);
266 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
267 status = HidP_GetCaps(ppd, &Caps);
268 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
269 data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
270
271 memset(&overlapped, 0, sizeof(overlapped));
272 overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
273 if (winetest_interactive)
274 {
275 max_time = READ_MAX_TIME;
276 timeout = 1000;
277 }
278 else
279 max_time = timeout = 100;
280 if (winetest_interactive)
281 trace("Test your device for the next %i seconds\n", max_time/1000);
282 report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
283 tick = GetTickCount();
284 spent = 0;
285 do
286 {
287 ReadFile(device, data, Caps.InputReportByteLength, NULL, &overlapped);
288 if (WaitForSingleObject(overlapped.hEvent, timeout) != WAIT_OBJECT_0)
289 {
290 ResetEvent(overlapped.hEvent);
291 spent = GetTickCount() - tick;
292 trace("REMAINING: %d ms\n", max_time - spent);
293 continue;
294 }
295 ResetEvent(overlapped.hEvent);
296 spent = GetTickCount() - tick;
297 GetOverlappedResult(device, &overlapped, &read, FALSE);
298 if (read)
299 {
300 int i;
301
302 report[0] = 0;
303 for (i = 0; i < read && i < Caps.InputReportByteLength; i++)
304 {
305 char bytestr[5];
306 sprintf(bytestr, "%x ", (BYTE)data[i]);
307 strcat(report, bytestr);
308 }
309 trace("Input report (%i): %s\n", read, report);
310
311 process_data(Caps, ppd, data, read);
312 }
313 trace("REMAINING: %d ms\n", max_time - spent);
314 } while(spent < max_time);
315
316 CloseHandle(overlapped.hEvent);
317 rc = HidD_FreePreparsedData(ppd);
318 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
319 CloseHandle(device);
320 HeapFree(GetProcessHeap(), 0, data);
321 HeapFree(GetProcessHeap(), 0, report);
322 }
323
324 static void test_get_input_report(void)
325 {
326 PHIDP_PREPARSED_DATA ppd;
327 HIDP_CAPS Caps;
328 WCHAR device_name[128];
329 CHAR *data = NULL;
330 DWORD tick, spent, max_time;
331 char *report;
332 BOOL rc;
333 NTSTATUS status;
334
335 USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
336 HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
337
338 if (!device)
339 device = get_device(0x0, NULL, 0x0, GENERIC_READ);
340
341 if (!device)
342 {
343 trace("No device found for testing\n");
344 return;
345 }
346 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
347 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
348 trace("HidD_GetInputRpeort tests on device :%s\n",wine_dbgstr_w(device_name));
349
350 rc = HidD_GetPreparsedData(device, &ppd);
351 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
352 status = HidP_GetCaps(ppd, &Caps);
353 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
354 data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
355
356 if (winetest_interactive)
357 max_time = READ_MAX_TIME;
358 else
359 max_time = 100;
360 if (winetest_interactive)
361 trace("Test your device for the next %i seconds\n", max_time/1000);
362 report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
363 tick = GetTickCount();
364 spent = 0;
365 do
366 {
367 int i;
368
369 data[0] = 0; /* Just testing report ID 0 for now, That will catch most devices */
370 rc = HidD_GetInputReport(device, data, Caps.InputReportByteLength);
371 spent = GetTickCount() - tick;
372
373 if (rc)
374 {
375 ok(data[0] == 0, "Report ID (0) is not the first byte of the data\n");
376 report[0] = 0;
377 for (i = 0; i < Caps.InputReportByteLength; i++)
378 {
379 char bytestr[5];
380 sprintf(bytestr, "%x ", (BYTE)data[i]);
381 strcat(report, bytestr);
382 }
383 trace("Input report (%i): %s\n", Caps.InputReportByteLength, report);
384
385 process_data(Caps, ppd, data, Caps.InputReportByteLength);
386 }
387 else
388 trace("Failed to get Input Report, (%x)\n", rc);
389 trace("REMAINING: %d ms\n", max_time - spent);
390 Sleep(500);
391 } while(spent < max_time);
392
393 rc = HidD_FreePreparsedData(ppd);
394 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
395 CloseHandle(device);
396 HeapFree(GetProcessHeap(), 0, data);
397 HeapFree(GetProcessHeap(), 0, report);
398 }
399
400 START_TEST(device)
401 {
402 run_for_each_device(test_device_info);
403 test_read_device();
404 test_get_input_report();
405 }