mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / hid / tests / device.c
blob3dd22b7f068344febdcdb4ea8eaac5db6f8ffee2
1 /*
2 * Copyright (c) 2017 Aric Stewart
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.
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.
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
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"
27 #include "wine/test.h"
29 #define READ_MAX_TIME 5000
31 typedef void (device_test)(HANDLE device);
33 static void test_device_info(HANDLE device)
35 PHIDP_PREPARSED_DATA ppd;
36 HIDP_CAPS Caps;
37 HIDD_ATTRIBUTES attributes;
38 HIDP_LINK_COLLECTION_NODE nodes[16];
39 ULONG nodes_count;
40 NTSTATUS status;
41 BOOL rc;
42 WCHAR device_name[128];
43 int i;
45 rc = HidD_GetPreparsedData(device, &ppd);
46 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
47 status = HidP_GetCaps(ppd, &Caps);
48 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
49 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
50 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
51 trace("Found device %s (%02x, %02x)\n", wine_dbgstr_w(device_name), Caps.UsagePage, Caps.Usage);
53 trace("LinkCollectionNodes: (%d)\n", Caps.NumberLinkCollectionNodes);
54 ok(Caps.NumberLinkCollectionNodes > 0, "Expected at least one link collection\n");
56 nodes_count = 0;
57 status = HidP_GetLinkCollectionNodes(nodes, &nodes_count, ppd);
58 ok(status == HIDP_STATUS_BUFFER_TOO_SMALL, "HidP_GetLinkCollectionNodes succeeded:%x\n", status);
60 nodes_count = ARRAY_SIZE(nodes);
61 status = HidP_GetLinkCollectionNodes(nodes, &nodes_count, ppd);
62 ok(status == HIDP_STATUS_SUCCESS, "HidP_GetLinkCollectionNodes failed:%x\n", status);
64 for (i = 0; i < nodes_count; ++i)
66 trace(" [%d] LinkUsage: %x LinkUsagePage: %x Parent: %x "
67 "NumberOfChildren: %x NextSibling: %x FirstChild: %x "
68 "CollectionType: %x IsAlias: %x UserContext: %p\n",
69 i, nodes[i].LinkUsage, nodes[i].LinkUsagePage, nodes[i].Parent,
70 nodes[i].NumberOfChildren, nodes[i].NextSibling, nodes[i].FirstChild,
71 nodes[i].CollectionType, nodes[i].IsAlias, nodes[i].UserContext);
74 ok(nodes_count > 0, "Unexpected number of link collection nodes:%u.\n", nodes_count);
75 ok(nodes[0].LinkUsagePage == Caps.UsagePage, "Unexpected top collection usage page:%x\n", nodes[0].LinkUsagePage);
76 ok(nodes[0].LinkUsage == Caps.Usage, "Unexpected top collection usage:%x\n", nodes[0].LinkUsage);
77 ok(nodes[0].CollectionType == 1, "Unexpected top collection type:%x\n", nodes[0].CollectionType);
79 rc = HidD_FreePreparsedData(ppd);
80 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
81 rc = HidD_GetAttributes(device, &attributes);
82 ok(rc, "Failed to get device attributes (0x%x)\n", GetLastError());
83 ok(attributes.Size == sizeof(attributes), "Unexpected HIDD_ATTRIBUTES size: %d\n", attributes.Size);
84 trace("Device attributes: vid:%04x pid:%04x ver:%04x\n", attributes.VendorID, attributes.ProductID, attributes.VersionNumber);
87 static void run_for_each_device(device_test *test)
89 GUID hid_guid;
90 HDEVINFO info_set;
91 DWORD index = 0;
92 SP_DEVICE_INTERFACE_DATA interface_data;
93 DWORD detail_size = MAX_PATH * sizeof(WCHAR);
94 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
96 HidD_GetHidGuid(&hid_guid);
98 ZeroMemory(&interface_data, sizeof(interface_data));
99 interface_data.cbSize = sizeof(interface_data);
101 data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data) + detail_size);
102 data->cbSize = sizeof(*data);
104 info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
105 while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
107 index ++;
109 if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
111 HANDLE file = CreateFileW(data->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
112 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_ACCESS_DENIED)
114 trace("Not enough permissions to read device %s.\n", wine_dbgstr_w(data->DevicePath));
115 continue;
117 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_SHARING_VIOLATION)
119 trace("Device is busy: %s.\n", wine_dbgstr_w(data->DevicePath));
120 continue;
123 ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u.\n",
124 wine_dbgstr_w(data->DevicePath), GetLastError());
126 if (file != INVALID_HANDLE_VALUE)
127 test(file);
129 CloseHandle(file);
132 HeapFree(GetProcessHeap(), 0, data);
133 SetupDiDestroyDeviceInfoList(info_set);
136 static HANDLE get_device(USHORT page, USHORT usages[], UINT usage_count, DWORD access)
138 GUID hid_guid;
139 HDEVINFO info_set;
140 DWORD index = 0;
141 SP_DEVICE_INTERFACE_DATA interface_data;
142 DWORD detail_size = MAX_PATH * sizeof(WCHAR);
143 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
144 NTSTATUS status;
145 BOOL rc;
147 HidD_GetHidGuid(&hid_guid);
149 ZeroMemory(&interface_data, sizeof(interface_data));
150 interface_data.cbSize = sizeof(interface_data);
152 data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
153 data->cbSize = sizeof(*data);
155 info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
156 while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
158 index ++;
160 if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
162 PHIDP_PREPARSED_DATA ppd;
163 HIDP_CAPS Caps;
164 HANDLE file = CreateFileW(data->DevicePath, access, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
165 if (file == INVALID_HANDLE_VALUE && GetLastError() == ERROR_ACCESS_DENIED)
167 trace("Not enough permissions to read device %s.\n", wine_dbgstr_w(data->DevicePath));
168 continue;
170 ok(file != INVALID_HANDLE_VALUE, "got error %u\n", GetLastError());
172 rc = HidD_GetPreparsedData(file, &ppd);
173 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
174 status = HidP_GetCaps(ppd, &Caps);
175 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
176 rc = HidD_FreePreparsedData(ppd);
177 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
178 if (!page || page == Caps.UsagePage)
180 int j;
181 if (!usage_count)
183 HeapFree(GetProcessHeap(), 0, data);
184 SetupDiDestroyDeviceInfoList(info_set);
185 return file;
187 for (j = 0; j < usage_count; j++)
188 if (!usages[j] || usages[j] == Caps.Usage)
190 HeapFree(GetProcessHeap(), 0, data);
191 SetupDiDestroyDeviceInfoList(info_set);
192 return file;
195 CloseHandle(file);
198 HeapFree(GetProcessHeap(), 0, data);
199 SetupDiDestroyDeviceInfoList(info_set);
200 return NULL;
203 static void process_data(HIDP_CAPS Caps, PHIDP_PREPARSED_DATA ppd, CHAR *data, DWORD data_length)
205 INT i;
206 NTSTATUS status;
208 if (Caps.NumberInputButtonCaps)
210 USAGE button_pages[100];
212 for (i = 1; i < 0xff; i++)
214 ULONG usage_length = 100;
215 status = HidP_GetUsages(HidP_Input, i, 0, button_pages, &usage_length, ppd, data, data_length);
216 ok (status == HIDP_STATUS_SUCCESS || usage_length == 0,
217 "HidP_GetUsages failed (%x) but usage length still %i\n", status, usage_length);
218 if (usage_length)
220 CHAR report[50];
221 int count;
222 int j;
224 count = usage_length;
225 j = 0;
226 report[0] = 0;
227 trace("\tButtons [0x%x: %i buttons]:\n", i, usage_length);
228 for (count = 0; count < usage_length; count += 15)
230 for (j=count; j < count+15 && j < usage_length; j++)
232 CHAR btn[7];
233 sprintf(btn, "%i ", button_pages[j]);
234 strcat(report, btn);
236 trace("\t\t%s\n", report);
242 if (Caps.NumberInputValueCaps)
244 ULONG value;
245 USHORT length;
246 HIDP_VALUE_CAPS *values = NULL;
248 values = HeapAlloc(GetProcessHeap(), 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps);
249 length = Caps.NumberInputValueCaps;
250 status = HidP_GetValueCaps(HidP_Input, values, &length, ppd);
251 ok(status == HIDP_STATUS_SUCCESS, "Failed to get value caps (%x)\n",status);
253 trace("\tValues:\n");
254 for (i = 0; i < length; i++)
256 ok(values[i].ReportCount, "Zero ReportCount for [%i,%i]\n", values[i].UsagePage, values[i].NotRange.Usage);
257 if (values[i].IsRange || values[i].ReportCount <= 1)
259 status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
260 values[i].Range.UsageMin, &value, ppd, data, data_length);
261 ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
262 values[i].UsagePage, values[i].Range.UsageMin, status);
263 trace("[%02x, %02x]: %u\n", values[i].UsagePage, values[i].Range.UsageMin, value);
265 else
267 USHORT k, array_size = (values[i].BitSize * values[i].ReportCount + 7) / 8;
268 PCHAR array = HeapAlloc(GetProcessHeap(), 0, array_size);
269 char *dump = HeapAlloc(GetProcessHeap(), 0, array_size * 3 + 1);
271 status = HidP_GetUsageValueArray(HidP_Input, values[i].UsagePage, 0,
272 values[i].NotRange.Usage, array, array_size, ppd, data, data_length);
273 ok(status == HIDP_STATUS_SUCCESS, "Failed to get value array [%i,%i] (%x)\n",
274 values[i].UsagePage, values[i].NotRange.Usage, status);
275 dump[0] = 0;
276 for (k = 0; k < array_size; k++)
278 char bytestr[5];
279 sprintf(bytestr, " %02x", (BYTE)array[k]);
280 strcat(dump, bytestr);
282 trace("[%02x, %02x] element bit size %u num elements %u:%s\n", values[i].UsagePage,
283 values[i].NotRange.Usage, values[i].BitSize, values[i].ReportCount, dump);
285 HeapFree(GetProcessHeap(), 0, dump);
286 HeapFree(GetProcessHeap(), 0, array);
290 HeapFree(GetProcessHeap(), 0, values);
294 static void test_read_device(void)
296 PHIDP_PREPARSED_DATA ppd;
297 HIDP_CAPS Caps;
298 OVERLAPPED overlapped;
299 WCHAR device_name[128];
300 CHAR *data = NULL;
301 DWORD read;
302 BOOL rc;
303 NTSTATUS status;
304 DWORD timeout, tick, spent, max_time;
305 char *report;
307 USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
308 HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
310 if (!device)
311 device = get_device(0x0, NULL, 0x0, GENERIC_READ);
313 if (!device)
315 trace("No device found for reading\n");
316 return;
318 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
319 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
320 trace("Read tests on device :%s\n",wine_dbgstr_w(device_name));
322 rc = HidD_GetPreparsedData(device, &ppd);
323 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
324 status = HidP_GetCaps(ppd, &Caps);
325 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
326 data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
328 memset(&overlapped, 0, sizeof(overlapped));
329 overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
330 if (winetest_interactive)
332 max_time = READ_MAX_TIME;
333 timeout = 1000;
335 else
336 max_time = timeout = 100;
337 if (winetest_interactive)
338 trace("Test your device for the next %i seconds\n", max_time/1000);
339 report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
340 tick = GetTickCount();
341 spent = 0;
344 ReadFile(device, data, Caps.InputReportByteLength, NULL, &overlapped);
345 if (WaitForSingleObject(overlapped.hEvent, timeout) != WAIT_OBJECT_0)
347 ResetEvent(overlapped.hEvent);
348 spent = GetTickCount() - tick;
349 trace("REMAINING: %d ms\n", max_time - spent);
350 continue;
352 ResetEvent(overlapped.hEvent);
353 spent = GetTickCount() - tick;
354 GetOverlappedResult(device, &overlapped, &read, FALSE);
355 if (read)
357 int i;
359 report[0] = 0;
360 for (i = 0; i < read && i < Caps.InputReportByteLength; i++)
362 char bytestr[5];
363 sprintf(bytestr, "%x ", (BYTE)data[i]);
364 strcat(report, bytestr);
366 trace("Input report (%i): %s\n", read, report);
368 process_data(Caps, ppd, data, read);
370 trace("REMAINING: %d ms\n", max_time - spent);
371 } while(spent < max_time);
373 CloseHandle(overlapped.hEvent);
374 rc = HidD_FreePreparsedData(ppd);
375 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
376 CancelIo(device);
377 CloseHandle(device);
378 HeapFree(GetProcessHeap(), 0, data);
379 HeapFree(GetProcessHeap(), 0, report);
382 static void test_get_input_report(void)
384 PHIDP_PREPARSED_DATA ppd;
385 HIDP_CAPS Caps;
386 WCHAR device_name[128];
387 CHAR *data = NULL;
388 DWORD tick, spent, max_time;
389 char *report;
390 BOOL rc;
391 NTSTATUS status;
393 USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
394 HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
396 if (!device)
397 device = get_device(0x0, NULL, 0x0, GENERIC_READ);
399 if (!device)
401 trace("No device found for testing\n");
402 return;
404 rc = HidD_GetProductString(device, device_name, sizeof(device_name));
405 ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
406 trace("HidD_GetInputRpeort tests on device :%s\n",wine_dbgstr_w(device_name));
408 rc = HidD_GetPreparsedData(device, &ppd);
409 ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
410 status = HidP_GetCaps(ppd, &Caps);
411 ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
412 data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
414 if (winetest_interactive)
415 max_time = READ_MAX_TIME;
416 else
417 max_time = 100;
418 if (winetest_interactive)
419 trace("Test your device for the next %i seconds\n", max_time/1000);
420 report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
421 tick = GetTickCount();
422 spent = 0;
425 int i;
427 data[0] = 0; /* Just testing report ID 0 for now, That will catch most devices */
428 rc = HidD_GetInputReport(device, data, Caps.InputReportByteLength);
429 spent = GetTickCount() - tick;
431 if (rc)
433 ok(data[0] == 0, "Report ID (0) is not the first byte of the data\n");
434 report[0] = 0;
435 for (i = 0; i < Caps.InputReportByteLength; i++)
437 char bytestr[5];
438 sprintf(bytestr, "%x ", (BYTE)data[i]);
439 strcat(report, bytestr);
441 trace("Input report (%i): %s\n", Caps.InputReportByteLength, report);
443 process_data(Caps, ppd, data, Caps.InputReportByteLength);
445 else
446 trace("Failed to get Input Report, (%x)\n", rc);
447 trace("REMAINING: %d ms\n", max_time - spent);
448 Sleep(500);
449 } while(spent < max_time);
451 rc = HidD_FreePreparsedData(ppd);
452 ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
453 CloseHandle(device);
454 HeapFree(GetProcessHeap(), 0, data);
455 HeapFree(GetProcessHeap(), 0, report);
458 START_TEST(device)
460 run_for_each_device(test_device_info);
461 test_read_device();
462 test_get_input_report();