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     NTSTATUS status;
38     BOOL rc;
39     WCHAR device_name[128];
40 
41     rc = HidD_GetPreparsedData(device, &ppd);
42     ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
43     status = HidP_GetCaps(ppd, &Caps);
44     ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
45     rc = HidD_GetProductString(device, device_name, sizeof(device_name));
46     ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
47     trace("Found device %s (%02x, %02x)\n", wine_dbgstr_w(device_name), Caps.UsagePage, Caps.Usage);
48     rc = HidD_FreePreparsedData(ppd);
49     ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
50 }
51 
52 static void run_for_each_device(device_test *test)
53 {
54     GUID hid_guid;
55     HDEVINFO info_set;
56     DWORD index = 0;
57     SP_DEVICE_INTERFACE_DATA interface_data;
58     DWORD detail_size = MAX_PATH * sizeof(WCHAR);
59     SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
60 
61     HidD_GetHidGuid(&hid_guid);
62 
63     ZeroMemory(&interface_data, sizeof(interface_data));
64     interface_data.cbSize = sizeof(interface_data);
65 
66     data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data) + detail_size);
67     data->cbSize = sizeof(*data);
68 
69     info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
70     while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
71     {
72         index ++;
73 
74         if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
75         {
76             HANDLE file = CreateFileW(data->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
77             if (file == INVALID_HANDLE_VALUE)
78             {
79                 trace("Failed to access device %s, likely not plugged in or access is denied.\n", wine_dbgstr_w(data->DevicePath));
80                 continue;
81             }
82 
83             test(file);
84 
85             CloseHandle(file);
86         }
87     }
88     HeapFree(GetProcessHeap(), 0, data);
89     SetupDiDestroyDeviceInfoList(info_set);
90 }
91 
92 static HANDLE get_device(USHORT page, USHORT usages[], UINT usage_count, DWORD access)
93 {
94     GUID hid_guid;
95     HDEVINFO info_set;
96     DWORD index = 0;
97     SP_DEVICE_INTERFACE_DATA interface_data;
98     DWORD detail_size = MAX_PATH * sizeof(WCHAR);
99     SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
100     NTSTATUS status;
101     BOOL rc;
102 
103     HidD_GetHidGuid(&hid_guid);
104 
105     ZeroMemory(&interface_data, sizeof(interface_data));
106     interface_data.cbSize = sizeof(interface_data);
107 
108     data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
109     data->cbSize = sizeof(*data);
110 
111     info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE);
112     while (SetupDiEnumDeviceInterfaces(info_set, NULL, &hid_guid, index, &interface_data))
113     {
114         index ++;
115 
116         if (SetupDiGetDeviceInterfaceDetailW(info_set, &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
117         {
118             PHIDP_PREPARSED_DATA ppd;
119             HIDP_CAPS Caps;
120             HANDLE file = CreateFileW(data->DevicePath, access, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
121             if (file == INVALID_HANDLE_VALUE)
122             {
123                 trace("Failed to access device %s, likely not plugged in or access is denied.\n", wine_dbgstr_w(data->DevicePath));
124                 continue;
125             }
126             rc = HidD_GetPreparsedData(file, &ppd);
127             ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
128             status = HidP_GetCaps(ppd, &Caps);
129             ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
130             rc = HidD_FreePreparsedData(ppd);
131             ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
132             if (!page || page == Caps.UsagePage)
133             {
134                 int j;
135                 if (!usage_count)
136                 {
137                     HeapFree(GetProcessHeap(), 0, data);
138                     SetupDiDestroyDeviceInfoList(info_set);
139                     return file;
140                 }
141                 for (j = 0; j < usage_count; j++)
142                     if (!usages[j] || usages[j] == Caps.Usage)
143                     {
144                         HeapFree(GetProcessHeap(), 0, data);
145                         SetupDiDestroyDeviceInfoList(info_set);
146                         return file;
147                     }
148             }
149             CloseHandle(file);
150         }
151     }
152     HeapFree(GetProcessHeap(), 0, data);
153     SetupDiDestroyDeviceInfoList(info_set);
154     return NULL;
155 }
156 
157 static void process_data(HIDP_CAPS Caps, PHIDP_PREPARSED_DATA ppd, CHAR *data, DWORD data_length)
158 {
159     INT i;
160     NTSTATUS status;
161 
162     if (Caps.NumberInputButtonCaps)
163     {
164         USAGE button_pages[100];
165 
166         for (i = 1; i < 0xff; i++)
167         {
168             ULONG usage_length = 100;
169             status = HidP_GetUsages(HidP_Input, i, 0, button_pages, &usage_length, ppd, data, data_length);
170             ok (status == HIDP_STATUS_SUCCESS || usage_length == 0,
171                 "HidP_GetUsages failed (%x) but usage length still %i\n", status, usage_length);
172             if (usage_length)
173             {
174                 CHAR report[50];
175                 int count;
176                 int j;
177 
178                 count = usage_length;
179                 j = 0;
180                 report[0] = 0;
181                 trace("\tButtons [0x%x: %i buttons]:\n", i, usage_length);
182                 for (count = 0; count < usage_length; count += 15)
183                 {
184                     for (j=count; j < count+15 && j < usage_length; j++)
185                     {
186                         CHAR btn[7];
187                         sprintf(btn, "%i ", button_pages[j]);
188                         strcat(report, btn);
189                     }
190                     trace("\t\t%s\n", report);
191                 }
192             }
193         }
194     }
195 
196     if (Caps.NumberInputValueCaps)
197     {
198         ULONG value;
199         USHORT length;
200         HIDP_VALUE_CAPS *values = NULL;
201 
202         values = HeapAlloc(GetProcessHeap(), 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps);
203         length = Caps.NumberInputValueCaps;
204         status = HidP_GetValueCaps(HidP_Input, values, &length, ppd);
205         ok(status == HIDP_STATUS_SUCCESS, "Failed to get value caps (%x)\n",status);
206 
207         trace("\tValues:\n");
208         for (i = 0; i < length; i++)
209         {
210             status = HidP_GetUsageValue(HidP_Input, values[i].UsagePage, 0,
211                 values[i].Range.UsageMin, &value, ppd, data, data_length);
212             ok(status == HIDP_STATUS_SUCCESS, "Failed to get value [%i,%i] (%x)\n",
213                 values[i].UsagePage, values[i].Range.UsageMin, status);
214             trace("[%02x, %02x]: %u\n",values[i].UsagePage, values[i].Range.UsageMin, value);
215         }
216 
217         HeapFree(GetProcessHeap(), 0, values);
218     }
219 }
220 
221 static void test_read_device(void)
222 {
223     PHIDP_PREPARSED_DATA ppd;
224     HIDP_CAPS Caps;
225     OVERLAPPED overlapped;
226     WCHAR device_name[128];
227     CHAR *data = NULL;
228     DWORD read;
229     BOOL rc;
230     NTSTATUS status;
231     DWORD timeout, tick, spent, max_time;
232     char *report;
233 
234     USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
235     HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
236 
237     if (!device)
238         device = get_device(0x0, NULL, 0x0, GENERIC_READ);
239 
240     if (!device)
241     {
242         trace("No device found for reading\n");
243         return;
244     }
245     rc = HidD_GetProductString(device, device_name, sizeof(device_name));
246     ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
247     trace("Read tests on device :%s\n",wine_dbgstr_w(device_name));
248 
249     rc = HidD_GetPreparsedData(device, &ppd);
250     ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
251     status = HidP_GetCaps(ppd, &Caps);
252     ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
253     data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
254 
255     memset(&overlapped, 0, sizeof(overlapped));
256     overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
257     if (winetest_interactive)
258     {
259         max_time = READ_MAX_TIME;
260         timeout = 1000;
261     }
262     else
263         max_time = timeout = 100;
264     if (winetest_interactive)
265         trace("Test your device for the next %i seconds\n", max_time/1000);
266     report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
267     tick = GetTickCount();
268     spent = 0;
269     do
270     {
271         ReadFile(device, data, Caps.InputReportByteLength, NULL, &overlapped);
272         if (WaitForSingleObject(overlapped.hEvent, timeout) != WAIT_OBJECT_0)
273         {
274             ResetEvent(overlapped.hEvent);
275             spent = GetTickCount() - tick;
276             trace("REMAINING: %d ms\n", max_time - spent);
277             continue;
278         }
279         ResetEvent(overlapped.hEvent);
280         spent = GetTickCount() - tick;
281         GetOverlappedResult(device, &overlapped, &read, FALSE);
282         if (read)
283         {
284             int i;
285 
286             report[0] = 0;
287             for (i = 0; i < read && i < Caps.InputReportByteLength; i++)
288             {
289                 char bytestr[5];
290                 sprintf(bytestr, "%x ", (BYTE)data[i]);
291                 strcat(report, bytestr);
292             }
293             trace("Input report (%i): %s\n", read, report);
294 
295             process_data(Caps, ppd, data, read);
296         }
297         trace("REMAINING: %d ms\n", max_time - spent);
298     } while(spent < max_time);
299 
300     CloseHandle(overlapped.hEvent);
301     rc = HidD_FreePreparsedData(ppd);
302     ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
303     CloseHandle(device);
304     HeapFree(GetProcessHeap(), 0, data);
305     HeapFree(GetProcessHeap(), 0, report);
306 }
307 
308 static void test_get_input_report(void)
309 {
310     PHIDP_PREPARSED_DATA ppd;
311     HIDP_CAPS Caps;
312     WCHAR device_name[128];
313     CHAR *data = NULL;
314     DWORD tick, spent, max_time;
315     char *report;
316     BOOL rc;
317     NTSTATUS status;
318 
319     USAGE device_usages[] = {HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_GENERIC_GAMEPAD};
320     HANDLE device = get_device(HID_USAGE_PAGE_GENERIC, device_usages, 2, GENERIC_READ);
321 
322     if (!device)
323         device = get_device(0x0, NULL, 0x0, GENERIC_READ);
324 
325     if (!device)
326     {
327         trace("No device found for testing\n");
328         return;
329     }
330     rc = HidD_GetProductString(device, device_name, sizeof(device_name));
331     ok(rc, "Failed to get product string(0x%x)\n", GetLastError());
332     trace("HidD_GetInputRpeort tests on device :%s\n",wine_dbgstr_w(device_name));
333 
334     rc = HidD_GetPreparsedData(device, &ppd);
335     ok(rc, "Failed to get preparsed data(0x%x)\n", GetLastError());
336     status = HidP_GetCaps(ppd, &Caps);
337     ok(status == HIDP_STATUS_SUCCESS, "Failed to get Caps(0x%x)\n", status);
338     data = HeapAlloc(GetProcessHeap(), 0, Caps.InputReportByteLength);
339 
340     if (winetest_interactive)
341         max_time = READ_MAX_TIME;
342     else
343         max_time = 100;
344     if (winetest_interactive)
345         trace("Test your device for the next %i seconds\n", max_time/1000);
346     report = HeapAlloc(GetProcessHeap(), 0, 3 * Caps.InputReportByteLength);
347     tick = GetTickCount();
348     spent = 0;
349     do
350     {
351         int i;
352 
353         data[0] = 0; /* Just testing report ID 0 for now, That will catch most devices */
354         rc = HidD_GetInputReport(device, data, Caps.InputReportByteLength);
355         spent = GetTickCount() - tick;
356 
357         if (rc)
358         {
359             ok(data[0] == 0, "Report ID (0) is not the first byte of the data\n");
360             report[0] = 0;
361             for (i = 0; i < Caps.InputReportByteLength; i++)
362             {
363                 char bytestr[5];
364                 sprintf(bytestr, "%x ", (BYTE)data[i]);
365                 strcat(report, bytestr);
366             }
367             trace("Input report (%i): %s\n", Caps.InputReportByteLength, report);
368 
369             process_data(Caps, ppd, data, Caps.InputReportByteLength);
370         }
371         else
372             trace("Failed to get Input Report, (%x)\n", rc);
373         trace("REMAINING: %d ms\n", max_time - spent);
374         Sleep(500);
375     } while(spent < max_time);
376 
377     rc = HidD_FreePreparsedData(ppd);
378     ok(rc, "Failed to free preparsed data(0x%x)\n", GetLastError());
379     CloseHandle(device);
380     HeapFree(GetProcessHeap(), 0, data);
381     HeapFree(GetProcessHeap(), 0, report);
382 }
383 
384 START_TEST(device)
385 {
386     run_for_each_device(test_device_info);
387     test_read_device();
388     test_get_input_report();
389 }
390