winebus.sys: Implement IOCTL_HID_GET_DEVICE_ATTRIBUTES for hid devices.
[wine.git] / dlls / hid / hidp.c
blob4a0fa64df1a4466eb45307774bec6e703d2fb26d
1 /*
2 * Human Input Devices
4 * Copyright (C) 2015 Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
23 #include <stdarg.h>
25 #define NONAMELESSUNION
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "winioctl.h"
32 #include "ddk/wdm.h"
34 #include "hidusage.h"
35 #include "ddk/hidpi.h"
36 #include "parse.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(hidp);
41 static NTSTATUS get_report_data(BYTE *report, INT reportLength, INT startBit, INT valueSize, PULONG value)
44 if ((startBit + valueSize) / 8 > reportLength)
45 return HIDP_STATUS_INVALID_REPORT_LENGTH;
47 if (valueSize == 1)
49 ULONG byte_index = startBit / 8;
50 ULONG bit_index = startBit - (byte_index * 8);
51 INT mask = (1 << bit_index);
52 *value = (report[byte_index] & mask);
54 else
56 ULONG byte_index = (startBit + valueSize - 1) / 8;
57 ULONG data = 0;
58 ULONG remainingBits = valueSize;
59 while (remainingBits)
61 data <<= 8;
63 if (remainingBits >= 8)
65 data |= report[byte_index];
66 byte_index --;
67 remainingBits -= 8;
69 else if (remainingBits > 0)
71 BYTE mask = ~(0xff << (8-remainingBits));
72 data |= report[byte_index] & mask;
73 remainingBits = 0;
76 *value = data;
78 return HIDP_STATUS_SUCCESS;
81 NTSTATUS WINAPI HidP_GetButtonCaps(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps,
82 PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData)
84 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
85 WINE_HID_REPORT *report = NULL;
86 USHORT b_count = 0, r_count = 0;
87 int i,j,u;
89 TRACE("(%i, %p, %p, %p)\n",ReportType, ButtonCaps, ButtonCapsLength, PreparsedData);
91 if (data->magic != HID_MAGIC)
92 return HIDP_STATUS_INVALID_PREPARSED_DATA;
94 switch(ReportType)
96 case HidP_Input:
97 b_count = data->caps.NumberInputButtonCaps;
98 r_count = data->dwInputReportCount;
99 report = HID_INPUT_REPORTS(data);
100 break;
101 case HidP_Output:
102 b_count = data->caps.NumberOutputButtonCaps;
103 r_count = data->dwOutputReportCount;
104 report = HID_OUTPUT_REPORTS(data);
105 break;
106 case HidP_Feature:
107 b_count = data->caps.NumberFeatureButtonCaps;
108 r_count = data->dwFeatureReportCount;
109 report = HID_FEATURE_REPORTS(data);
110 break;
111 default:
112 return HIDP_STATUS_INVALID_REPORT_TYPE;
115 if (!r_count || !b_count || !report)
117 *ButtonCapsLength = 0;
118 return HIDP_STATUS_SUCCESS;
121 b_count = min(b_count, *ButtonCapsLength);
123 u = 0;
124 for (j = 0; j < r_count && u < b_count; j++)
126 for (i = 0; i < report->elementCount && u < b_count; i++)
128 if (report->Elements[i].ElementType == ButtonElement)
129 ButtonCaps[u++] = report->Elements[i].caps.button;
131 report = HID_NEXT_REPORT(data, report);
134 *ButtonCapsLength = b_count;
135 return HIDP_STATUS_SUCCESS;
139 NTSTATUS WINAPI HidP_GetCaps(PHIDP_PREPARSED_DATA PreparsedData,
140 PHIDP_CAPS Capabilities)
142 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
144 TRACE("(%p, %p)\n",PreparsedData, Capabilities);
146 if (data->magic != HID_MAGIC)
147 return HIDP_STATUS_INVALID_PREPARSED_DATA;
149 *Capabilities = data->caps;
151 return HIDP_STATUS_SUCCESS;
154 static NTSTATUS find_value(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
155 USAGE Usage, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report,
156 WINE_HID_ELEMENT **element)
158 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
159 WINE_HID_REPORT *report = NULL;
160 USHORT v_count = 0, r_count = 0;
161 int i;
163 TRACE("(%i, %x, %i, %i, %p, %p)\n", ReportType, UsagePage, LinkCollection, Usage,
164 PreparsedData, Report);
166 if (data->magic != HID_MAGIC)
167 return HIDP_STATUS_INVALID_PREPARSED_DATA;
168 switch(ReportType)
170 case HidP_Input:
171 v_count = data->caps.NumberInputValueCaps;
172 r_count = data->dwInputReportCount;
173 report = HID_INPUT_REPORTS(data);
174 break;
175 case HidP_Output:
176 v_count = data->caps.NumberOutputValueCaps;
177 r_count = data->dwOutputReportCount;
178 report = HID_OUTPUT_REPORTS(data);
179 break;
180 case HidP_Feature:
181 v_count = data->caps.NumberFeatureValueCaps;
182 r_count = data->dwFeatureReportCount;
183 report = HID_FEATURE_REPORTS(data);
184 break;
185 default:
186 return HIDP_STATUS_INVALID_REPORT_TYPE;
189 if (!r_count || !v_count || !report)
190 return HIDP_STATUS_USAGE_NOT_FOUND;
192 for (i = 0; i < r_count; i++)
194 if (!report->reportID || report->reportID == Report[0])
195 break;
196 report = HID_NEXT_REPORT(data, report);
199 if (i == r_count)
200 return HIDP_STATUS_REPORT_DOES_NOT_EXIST;
202 for (i = 0; i < report->elementCount; i++)
204 if (report->Elements[i].ElementType == ValueElement &&
205 report->Elements[i].caps.value.UsagePage == UsagePage &&
206 report->Elements[i].caps.value.u.NotRange.Usage == Usage)
208 *element = &report->Elements[i];
209 return HIDP_STATUS_SUCCESS;
213 return HIDP_STATUS_USAGE_NOT_FOUND;
216 NTSTATUS WINAPI HidP_GetScaledUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage,
217 USHORT LinkCollection, USAGE Usage, PLONG UsageValue,
218 PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength)
220 NTSTATUS rc;
221 WINE_HID_ELEMENT *element;
222 TRACE("(%i, %x, %i, %i, %p, %p, %p, %i)\n", ReportType, UsagePage, LinkCollection, Usage, UsageValue,
223 PreparsedData, Report, ReportLength);
225 rc = find_value(ReportType, UsagePage, LinkCollection, Usage, PreparsedData, Report, &element);
227 if (rc == HIDP_STATUS_SUCCESS)
229 ULONG rawValue;
230 rc = get_report_data((BYTE*)Report, ReportLength,
231 element->valueStartBit, element->bitCount, &rawValue);
232 if (rc != HIDP_STATUS_SUCCESS)
233 return rc;
234 if (element->caps.value.BitSize == 16)
235 rawValue = (short)rawValue;
236 *UsageValue = rawValue;
239 return rc;
243 NTSTATUS WINAPI HidP_GetUsageValue(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
244 USAGE Usage, PULONG UsageValue, PHIDP_PREPARSED_DATA PreparsedData,
245 PCHAR Report, ULONG ReportLength)
247 WINE_HID_ELEMENT *element;
248 NTSTATUS rc;
250 TRACE("(%i, %x, %i, %i, %p, %p, %p, %i)\n", ReportType, UsagePage, LinkCollection, Usage, UsageValue,
251 PreparsedData, Report, ReportLength);
253 rc = find_value(ReportType, UsagePage, LinkCollection, Usage, PreparsedData, Report, &element);
255 if (rc == HIDP_STATUS_SUCCESS)
257 return get_report_data((BYTE*)Report, ReportLength,
258 element->valueStartBit, element->bitCount, UsageValue);
261 return rc;
265 NTSTATUS WINAPI HidP_GetUsages(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, USHORT LinkCollection,
266 PUSAGE UsageList, PULONG UsageLength, PHIDP_PREPARSED_DATA PreparsedData,
267 PCHAR Report, ULONG ReportLength)
269 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
270 WINE_HID_REPORT *report = NULL;
271 BOOL found = FALSE;
272 USHORT b_count = 0, r_count = 0;
273 int i,uCount;
275 TRACE("(%i, %x, %i, %p, %p, %p, %p, %i)\n", ReportType, UsagePage, LinkCollection, UsageList,
276 UsageLength, PreparsedData, Report, ReportLength);
278 if (data->magic != HID_MAGIC)
279 return HIDP_STATUS_INVALID_PREPARSED_DATA;
281 switch(ReportType)
283 case HidP_Input:
284 b_count = data->caps.NumberInputButtonCaps;
285 r_count = data->dwInputReportCount;
286 report = HID_INPUT_REPORTS(data);
287 break;
288 case HidP_Output:
289 b_count = data->caps.NumberOutputButtonCaps;
290 r_count = data->dwOutputReportCount;
291 report = HID_OUTPUT_REPORTS(data);
292 break;
293 case HidP_Feature:
294 b_count = data->caps.NumberFeatureButtonCaps;
295 r_count = data->dwFeatureReportCount;
296 report = HID_FEATURE_REPORTS(data);
297 break;
298 default:
299 return HIDP_STATUS_INVALID_REPORT_TYPE;
302 if (!r_count || !b_count || !report)
303 return HIDP_STATUS_USAGE_NOT_FOUND;
305 for (i = 0; i < r_count; i++)
307 if (!report->reportID || report->reportID == Report[0])
308 break;
309 report = HID_NEXT_REPORT(data, report);
312 if (i == r_count)
313 return HIDP_STATUS_REPORT_DOES_NOT_EXIST;
315 uCount = 0;
316 for (i = 0; i < report->elementCount && uCount < *UsageLength; i++)
318 if (report->Elements[i].ElementType == ButtonElement &&
319 report->Elements[i].caps.button.UsagePage == UsagePage)
321 int k;
322 WINE_HID_ELEMENT *element = &report->Elements[i];
323 for (k=0; k < element->bitCount; k++)
325 UINT v = 0;
326 NTSTATUS rc = get_report_data((BYTE*)Report, ReportLength,
327 element->valueStartBit + k, 1, &v);
328 if (rc != HIDP_STATUS_SUCCESS)
329 return rc;
330 found = TRUE;
331 if (v)
333 if (uCount == *UsageLength)
334 return HIDP_STATUS_BUFFER_TOO_SMALL;
335 UsageList[uCount] = element->caps.button.u.Range.UsageMin + k;
336 uCount++;
342 if (!found)
343 return HIDP_STATUS_USAGE_NOT_FOUND;
345 *UsageLength = uCount;
347 return HIDP_STATUS_SUCCESS;
351 NTSTATUS WINAPI HidP_GetValueCaps(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps,
352 PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData)
354 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
355 WINE_HID_REPORT *report = NULL;
356 USHORT v_count = 0, r_count = 0;
357 int i,j,u;
359 TRACE("(%i, %p, %p, %p)\n", ReportType, ValueCaps, ValueCapsLength, PreparsedData);
361 if (data->magic != HID_MAGIC)
362 return HIDP_STATUS_INVALID_PREPARSED_DATA;
364 switch(ReportType)
366 case HidP_Input:
367 v_count = data->caps.NumberInputValueCaps;
368 r_count = data->dwInputReportCount;
369 report = HID_INPUT_REPORTS(data);
370 break;
371 case HidP_Output:
372 v_count = data->caps.NumberOutputValueCaps;
373 r_count = data->dwOutputReportCount;
374 report = HID_OUTPUT_REPORTS(data);
375 break;
376 case HidP_Feature:
377 v_count = data->caps.NumberFeatureValueCaps;
378 r_count = data->dwFeatureReportCount;
379 report = HID_FEATURE_REPORTS(data);
380 break;
381 default:
382 return HIDP_STATUS_INVALID_REPORT_TYPE;
385 if (!r_count || !v_count || !report)
387 *ValueCapsLength = 0;
388 return HIDP_STATUS_SUCCESS;
391 v_count = min(v_count, *ValueCapsLength);
393 u = 0;
394 for (j = 0; j < r_count && u < v_count; j++)
396 for (i = 0; i < report->elementCount && u < v_count; i++)
398 if (report->Elements[i].ElementType == ValueElement)
399 ValueCaps[u++] = report->Elements[i].caps.value;
401 report = HID_NEXT_REPORT(data, report);
404 *ValueCapsLength = v_count;
405 return HIDP_STATUS_SUCCESS;
408 NTSTATUS WINAPI HidP_InitializeReportForID(HIDP_REPORT_TYPE ReportType, UCHAR ReportID,
409 PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report,
410 ULONG ReportLength)
412 int size;
413 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
414 WINE_HID_REPORT *report = NULL;
415 BOOL found=FALSE;
416 int r_count;
417 int i;
419 TRACE("(%i, %i, %p, %p, %i)\n",ReportType, ReportID, PreparsedData, Report, ReportLength);
421 if (data->magic != HID_MAGIC)
422 return HIDP_STATUS_INVALID_PREPARSED_DATA;
424 switch(ReportType)
426 case HidP_Input:
427 size = data->caps.InputReportByteLength;
428 report = HID_INPUT_REPORTS(data);
429 r_count = data->dwInputReportCount;
430 break;
431 case HidP_Output:
432 size = data->caps.OutputReportByteLength;
433 report = HID_OUTPUT_REPORTS(data);
434 r_count = data->dwOutputReportCount;
435 break;
436 case HidP_Feature:
437 size = data->caps.FeatureReportByteLength;
438 report = HID_FEATURE_REPORTS(data);
439 r_count = data->dwFeatureReportCount;
440 break;
441 default:
442 return HIDP_STATUS_INVALID_REPORT_TYPE;
445 if (!r_count || !size || !report)
446 return HIDP_STATUS_REPORT_DOES_NOT_EXIST;
448 if (size != ReportLength)
449 return HIDP_STATUS_INVALID_REPORT_LENGTH;
451 ZeroMemory(Report, size);
453 for (i = 0; i < r_count; i++)
455 if (report->reportID == ReportID)
457 found = TRUE;
458 if (report->reportID)
459 Report[0] = ReportID;
460 /* TODO: Handle null and default values */
462 report = HID_NEXT_REPORT(data, report);
465 if (!found)
466 return HIDP_STATUS_REPORT_DOES_NOT_EXIST;
468 return HIDP_STATUS_SUCCESS;
471 ULONG WINAPI HidP_MaxUsageListLength(HIDP_REPORT_TYPE ReportType, USAGE UsagePage, PHIDP_PREPARSED_DATA PreparsedData)
473 PWINE_HIDP_PREPARSED_DATA data = (PWINE_HIDP_PREPARSED_DATA)PreparsedData;
474 WINE_HID_REPORT *report = NULL;
475 int r_count;
476 int i;
477 int count = 0;
479 TRACE("(%i, %x, %p)\n", ReportType, UsagePage, PreparsedData);
481 if (data->magic != HID_MAGIC)
482 return 0;
484 switch(ReportType)
486 case HidP_Input:
487 report = HID_INPUT_REPORTS(data);
488 r_count = data->dwInputReportCount;
489 break;
490 case HidP_Output:
491 report = HID_OUTPUT_REPORTS(data);
492 r_count = data->dwOutputReportCount;
493 break;
494 case HidP_Feature:
495 report = HID_FEATURE_REPORTS(data);
496 r_count = data->dwFeatureReportCount;
497 break;
498 default:
499 return HIDP_STATUS_INVALID_REPORT_TYPE;
502 if (!r_count || !report)
503 return 0;
505 for (i = 0; i < r_count; i++)
507 int j;
508 for (j = 0; j < report->elementCount; j++)
510 if (report->Elements[j].caps.button.UsagePage == UsagePage)
512 if (report->Elements[j].caps.button.IsRange)
513 count += (report->Elements[j].caps.button.u.Range.UsageMax -
514 report->Elements[j].caps.button.u.Range.UsageMin) + 1;
515 else
516 count++;
519 report = HID_NEXT_REPORT(data, report);
521 return count;
524 NTSTATUS WINAPI HidP_TranslateUsagesToI8042ScanCodes(USAGE *ChangedUsageList,
525 ULONG UsageListLength, HIDP_KEYBOARD_DIRECTION KeyAction,
526 HIDP_KEYBOARD_MODIFIER_STATE *ModifierState,
527 PHIDP_INSERT_SCANCODES InsertCodesProcedure, VOID *InsertCodesContext)
529 FIXME("stub: %p, %i, %i, %p, %p, %p\n", ChangedUsageList, UsageListLength,
530 KeyAction, ModifierState, InsertCodesProcedure, InsertCodesContext);
532 return STATUS_NOT_IMPLEMENTED;