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