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