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 }
406