imm32/tests: Test ImmTranslateMessage / ImeToAsciiEx calls.
[wine.git] / dlls / hidclass.sys / device.c
blobac201afeddfa1c4637f9e3852d746d6e32015720
1 /*
2 * HIDClass device functions
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 <stdarg.h>
22 #include <stdlib.h>
23 #include "hid.h"
24 #include "winreg.h"
25 #include "winuser.h"
27 #include "ddk/hidsdi.h"
28 #include "ddk/hidtypes.h"
29 #include "ddk/wdm.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(hid);
36 static void WINAPI read_cancel_routine(DEVICE_OBJECT *device, IRP *irp)
38 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
39 KIRQL irql;
41 TRACE("cancel %p IRP on device %p\n", irp, device);
43 IoReleaseCancelSpinLock(irp->CancelIrql);
45 KeAcquireSpinLock( &queue->lock, &irql );
47 RemoveEntryList(&irp->Tail.Overlay.ListEntry);
49 KeReleaseSpinLock( &queue->lock, irql );
51 irp->IoStatus.Status = STATUS_CANCELLED;
52 irp->IoStatus.Information = 0;
53 IoCompleteRequest(irp, IO_NO_INCREMENT);
56 static struct hid_report *hid_report_create( HID_XFER_PACKET *packet, ULONG length )
58 struct hid_report *report;
60 if (!(report = malloc( offsetof( struct hid_report, buffer[length] ) )))
61 return NULL;
62 report->ref = 1;
63 report->length = length;
64 memcpy( report->buffer, packet->reportBuffer, packet->reportBufferLen );
65 memset( report->buffer + packet->reportBufferLen, 0, length - packet->reportBufferLen );
67 return report;
70 static void hid_report_incref( struct hid_report *report )
72 InterlockedIncrement( &report->ref );
75 static void hid_report_decref( struct hid_report *report )
77 if (!report) return;
78 if (InterlockedDecrement( &report->ref ) == 0) free( report );
81 static struct hid_queue *hid_queue_create( void )
83 struct hid_queue *queue;
85 if (!(queue = calloc( 1, sizeof(struct hid_queue) ))) return NULL;
86 InitializeListHead( &queue->irp_queue );
87 KeInitializeSpinLock( &queue->lock );
88 list_init( &queue->entry );
89 queue->length = 32;
90 queue->read_idx = 0;
91 queue->write_idx = 0;
93 return queue;
96 static IRP *hid_queue_pop_irp( struct hid_queue *queue )
98 LIST_ENTRY *entry;
99 IRP *irp = NULL;
100 KIRQL irql;
102 KeAcquireSpinLock( &queue->lock, &irql );
104 while (!irp && (entry = RemoveHeadList( &queue->irp_queue )) != &queue->irp_queue)
106 irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
107 if (!IoSetCancelRoutine( irp, NULL ))
109 /* cancel routine is already cleared, meaning that it was called. let it handle completion. */
110 InitializeListHead( &irp->Tail.Overlay.ListEntry );
111 irp = NULL;
115 KeReleaseSpinLock( &queue->lock, irql );
116 return irp;
119 void hid_queue_remove_pending_irps( struct hid_queue *queue )
121 IRP *irp;
123 while ((irp = hid_queue_pop_irp( queue )))
125 irp->IoStatus.Status = STATUS_DELETE_PENDING;
126 IoCompleteRequest( irp, IO_NO_INCREMENT );
130 void hid_queue_destroy( struct hid_queue *queue )
132 hid_queue_remove_pending_irps( queue );
133 while (queue->length--) hid_report_decref( queue->reports[queue->length] );
134 list_remove( &queue->entry );
135 free( queue );
138 static NTSTATUS hid_queue_resize( struct hid_queue *queue, ULONG length )
140 struct hid_report *old_reports[512];
141 LONG old_length = queue->length;
142 KIRQL irql;
144 if (length < 2 || length > 512) return STATUS_INVALID_PARAMETER;
145 if (length == queue->length) return STATUS_SUCCESS;
147 KeAcquireSpinLock( &queue->lock, &irql );
148 memcpy( old_reports, queue->reports, old_length * sizeof(void *) );
149 memset( queue->reports, 0, old_length * sizeof(void *) );
150 queue->length = length;
151 queue->write_idx = 0;
152 queue->read_idx = 0;
153 KeReleaseSpinLock( &queue->lock, irql );
155 while (old_length--) hid_report_decref( old_reports[old_length] );
156 return STATUS_SUCCESS;
159 static NTSTATUS hid_queue_push_irp( struct hid_queue *queue, IRP *irp )
161 KIRQL irql;
163 KeAcquireSpinLock( &queue->lock, &irql );
165 IoSetCancelRoutine( irp, read_cancel_routine );
166 if (irp->Cancel && !IoSetCancelRoutine( irp, NULL ))
168 /* IRP was canceled before we set cancel routine */
169 InitializeListHead( &irp->Tail.Overlay.ListEntry );
170 KeReleaseSpinLock( &queue->lock, irql );
171 return STATUS_CANCELLED;
174 InsertTailList( &queue->irp_queue, &irp->Tail.Overlay.ListEntry );
175 irp->IoStatus.Status = STATUS_PENDING;
176 IoMarkIrpPending( irp );
178 KeReleaseSpinLock( &queue->lock, irql );
179 return STATUS_PENDING;
182 static void hid_queue_push_report( struct hid_queue *queue, struct hid_report *report )
184 ULONG i = queue->write_idx, next = i + 1;
185 struct hid_report *prev;
186 KIRQL irql;
188 if (next >= queue->length) next = 0;
189 hid_report_incref( report );
191 KeAcquireSpinLock( &queue->lock, &irql );
192 prev = queue->reports[i];
193 queue->reports[i] = report;
194 if (next == queue->read_idx) queue->read_idx = next + 1;
195 if (queue->read_idx >= queue->length) queue->read_idx = 0;
196 KeReleaseSpinLock( &queue->lock, irql );
198 hid_report_decref( prev );
199 queue->write_idx = next;
202 static struct hid_report *hid_queue_pop_report( struct hid_queue *queue )
204 ULONG i = queue->read_idx, next = i + 1;
205 struct hid_report *report;
206 KIRQL irql;
208 if (next >= queue->length) next = 0;
210 KeAcquireSpinLock( &queue->lock, &irql );
211 report = queue->reports[i];
212 queue->reports[i] = NULL;
213 if (i != queue->write_idx) queue->read_idx = next;
214 KeReleaseSpinLock( &queue->lock, irql );
216 return report;
219 static void hid_device_queue_input( DEVICE_OBJECT *device, HID_XFER_PACKET *packet )
221 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
222 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
223 const BOOL polled = ext->u.pdo.information.Polled;
224 ULONG size, report_len = polled ? packet->reportBufferLen : desc->InputLength;
225 struct hid_report *last_report, *report;
226 struct hid_queue *queue;
227 LIST_ENTRY completed, *entry;
228 RAWINPUT *rawinput;
229 KIRQL irql;
230 IRP *irp;
232 TRACE("device %p, packet %p\n", device, packet);
234 if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID ))
236 size = offsetof( RAWINPUT, data.hid.bRawData[report_len] );
237 if (!(rawinput = malloc( size ))) ERR( "Failed to allocate rawinput data!\n" );
238 else
240 INPUT input;
242 rawinput->header.dwType = RIM_TYPEHID;
243 rawinput->header.dwSize = size;
244 rawinput->header.hDevice = ULongToHandle( ext->u.pdo.rawinput_handle );
245 rawinput->header.wParam = RIM_INPUT;
246 rawinput->data.hid.dwCount = 1;
247 rawinput->data.hid.dwSizeHid = report_len;
248 memcpy( rawinput->data.hid.bRawData, packet->reportBuffer, packet->reportBufferLen );
249 memset( rawinput->data.hid.bRawData + packet->reportBufferLen, 0, report_len - packet->reportBufferLen );
251 input.type = INPUT_HARDWARE;
252 input.hi.uMsg = WM_INPUT;
253 input.hi.wParamH = 0;
254 input.hi.wParamL = 0;
255 __wine_send_input( 0, &input, rawinput );
257 free( rawinput );
261 if (!(last_report = hid_report_create( packet, report_len )))
263 ERR( "Failed to allocate hid_report!\n" );
264 return;
267 InitializeListHead( &completed );
269 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
270 LIST_FOR_EACH_ENTRY( queue, &ext->u.pdo.queues, struct hid_queue, entry )
272 if (!polled) hid_queue_push_report( queue, last_report );
276 if (!(irp = hid_queue_pop_irp( queue ))) break;
277 if (!(report = hid_queue_pop_report( queue ))) hid_report_incref( (report = last_report) );
279 memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, report->length );
280 irp->IoStatus.Information = report->length;
281 irp->IoStatus.Status = STATUS_SUCCESS;
282 hid_report_decref( report );
284 InsertTailList( &completed, &irp->Tail.Overlay.ListEntry );
286 while (polled);
288 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
290 while ((entry = RemoveHeadList( &completed )) != &completed)
292 irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
293 IoCompleteRequest( irp, IO_NO_INCREMENT );
296 hid_report_decref( last_report );
299 static HIDP_REPORT_IDS *find_report_with_type_and_id( BASE_DEVICE_EXTENSION *ext, BYTE type, BYTE id, BOOL any_id )
301 HIDP_REPORT_IDS *report, *reports = ext->u.pdo.device_desc.ReportIDs;
302 ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength;
304 for (report = reports; report != reports + report_count; report++)
306 if (!any_id && report->ReportID && report->ReportID != id) continue;
307 if (type == HidP_Input && report->InputLength) return report;
308 if (type == HidP_Output && report->OutputLength) return report;
309 if (type == HidP_Feature && report->FeatureLength) return report;
312 return NULL;
315 static DWORD CALLBACK hid_device_thread(void *args)
317 DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
318 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
319 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
320 BOOL polled = ext->u.pdo.information.Polled;
321 HIDP_REPORT_IDS *report;
322 HID_XFER_PACKET *packet;
323 ULONG report_id = 0;
324 IO_STATUS_BLOCK io;
325 BYTE *buffer;
326 DWORD res;
328 packet = malloc( sizeof(*packet) + desc->InputLength );
329 buffer = (BYTE *)(packet + 1);
331 report = find_report_with_type_and_id( ext, HidP_Input, 0, TRUE );
332 if (!report) WARN("no input report found.\n");
333 else report_id = report->ReportID;
337 packet->reportId = buffer[0] = report_id;
338 packet->reportBuffer = buffer;
339 packet->reportBufferLen = desc->InputLength;
341 if (!report_id)
343 packet->reportBuffer++;
344 packet->reportBufferLen--;
347 call_minidriver( IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo, NULL, 0,
348 packet->reportBuffer, packet->reportBufferLen, &io );
350 if (io.Status == STATUS_SUCCESS)
352 if (!report_id) io.Information++;
353 if (!(report = find_report_with_type_and_id( ext, HidP_Input, buffer[0], FALSE )))
354 WARN( "dropping unknown input id %u\n", buffer[0] );
355 else if (!polled && io.Information < report->InputLength)
356 WARN( "dropping short report, len %Iu expected %u\n", io.Information, report->InputLength );
357 else
359 packet->reportId = buffer[0];
360 packet->reportBuffer = buffer;
361 packet->reportBufferLen = io.Information;
362 hid_device_queue_input( device, packet );
366 res = WaitForSingleObject(ext->u.pdo.halt_event, polled ? ext->u.pdo.poll_interval : 0);
367 } while (res == WAIT_TIMEOUT);
369 TRACE( "device thread exiting, res %#lx\n", res );
370 return 1;
373 void HID_StartDeviceThread(DEVICE_OBJECT *device)
375 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
376 ext->u.pdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL);
377 ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL);
380 struct device_strings
382 const WCHAR *id;
383 const WCHAR *product;
386 static const struct device_strings device_strings[] =
388 /* Microsoft controllers */
389 { .id = L"VID_045E&PID_028E", .product = L"Controller (XBOX 360 For Windows)" },
390 { .id = L"VID_045E&PID_028F", .product = L"Controller (XBOX 360 For Windows)" },
391 { .id = L"VID_045E&PID_02D1", .product = L"Controller (Xbox One For Windows)" },
392 { .id = L"VID_045E&PID_02DD", .product = L"Controller (Xbox One For Windows)" },
393 { .id = L"VID_045E&PID_02E3", .product = L"Controller (Xbox One For Windows)" },
394 { .id = L"VID_045E&PID_02EA", .product = L"Controller (Xbox One For Windows)" },
395 { .id = L"VID_045E&PID_02FD", .product = L"Controller (Xbox One For Windows)" },
396 { .id = L"VID_045E&PID_0719", .product = L"Controller (XBOX 360 For Windows)" },
397 { .id = L"VID_045E&PID_0B00", .product = L"Controller (Xbox One For Windows)" },
398 { .id = L"VID_045E&PID_0B05", .product = L"Controller (Xbox One For Windows)" },
399 { .id = L"VID_045E&PID_0B12", .product = L"Controller (Xbox One For Windows)" },
400 { .id = L"VID_045E&PID_0B13", .product = L"Controller (Xbox One For Windows)" },
401 /* Sony controllers */
402 { .id = L"VID_054C&PID_05C4", .product = L"Wireless Controller" },
403 { .id = L"VID_054C&PID_09CC", .product = L"Wireless Controller" },
404 { .id = L"VID_054C&PID_0BA0", .product = L"Wireless Controller" },
405 { .id = L"VID_054C&PID_0CE6", .product = L"Wireless Controller" },
408 static const WCHAR *find_device_string( const WCHAR *device_id, ULONG index )
410 const WCHAR *match_id = wcsrchr( device_id, '\\' ) + 1;
411 DWORD i;
413 if (index != HID_STRING_ID_IPRODUCT) return NULL;
415 for (i = 0; i < ARRAY_SIZE(device_strings); ++i)
416 if (!wcsnicmp( device_strings[i].id, match_id, 17 ))
417 return device_strings[i].product;
419 return NULL;
422 struct completion_params
424 HID_XFER_PACKET packet;
425 ULONG padding;
426 IRP *irp;
429 static NTSTATUS CALLBACK xfer_completion( DEVICE_OBJECT *device, IRP *irp, void *context )
431 struct completion_params *params = context;
432 IRP *orig_irp = params->irp;
434 TRACE( "device %p, irp %p, context %p\n", device, irp, context );
436 orig_irp->IoStatus = irp->IoStatus;
437 orig_irp->IoStatus.Information -= params->padding;
438 IoCompleteRequest( orig_irp, IO_NO_INCREMENT );
440 free( params );
441 return STATUS_SUCCESS;
444 static NTSTATUS hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp )
446 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
447 ULONG offset, report_len = 0, buffer_len = 0;
448 struct completion_params *params;
449 HIDP_REPORT_IDS *report = NULL;
450 BYTE *buffer = NULL;
452 switch (code)
454 case IOCTL_HID_GET_FEATURE:
455 case IOCTL_HID_GET_INPUT_REPORT:
456 buffer_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
457 buffer = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority );
458 break;
459 case IOCTL_HID_SET_FEATURE:
460 case IOCTL_HID_SET_OUTPUT_REPORT:
461 buffer_len = stack->Parameters.DeviceIoControl.InputBufferLength;
462 buffer = irp->AssociatedIrp.SystemBuffer;
463 break;
464 case IOCTL_HID_WRITE_REPORT:
465 buffer_len = stack->Parameters.Write.Length;
466 buffer = irp->AssociatedIrp.SystemBuffer;
467 break;
469 if (!buffer || !buffer_len) return STATUS_INVALID_USER_BUFFER;
471 switch (code)
473 case IOCTL_HID_GET_INPUT_REPORT:
474 report = find_report_with_type_and_id( ext, HidP_Input, buffer[0], FALSE );
475 if (report) report_len = report->InputLength;
476 break;
477 case IOCTL_HID_SET_OUTPUT_REPORT:
478 case IOCTL_HID_WRITE_REPORT:
479 report = find_report_with_type_and_id( ext, HidP_Output, buffer[0], FALSE );
480 if (report) report_len = report->OutputLength;
481 break;
482 case IOCTL_HID_GET_FEATURE:
483 case IOCTL_HID_SET_FEATURE:
484 report = find_report_with_type_and_id( ext, HidP_Feature, buffer[0], FALSE );
485 if (report) report_len = report->FeatureLength;
486 break;
488 if (!report || buffer_len < report_len) return STATUS_INVALID_PARAMETER;
489 offset = report->ReportID ? 0 : 1;
491 if (!(params = calloc( 1, sizeof(struct completion_params) ))) return STATUS_NO_MEMORY;
492 params->packet.reportId = report->ReportID;
493 params->packet.reportBuffer = buffer + offset;
494 params->irp = irp;
496 switch (code)
498 case IOCTL_HID_GET_FEATURE:
499 case IOCTL_HID_GET_INPUT_REPORT:
500 params->packet.reportBufferLen = buffer_len - offset;
501 irp = IoBuildDeviceIoControlRequest( code, ext->u.pdo.parent_fdo, NULL, 0, &params->packet,
502 sizeof(params->packet), TRUE, NULL, NULL );
503 break;
504 case IOCTL_HID_WRITE_REPORT:
505 params->padding = 1 - offset;
506 /* fallthrough */
507 case IOCTL_HID_SET_FEATURE:
508 case IOCTL_HID_SET_OUTPUT_REPORT:
509 params->packet.reportBufferLen = report_len - offset;
510 irp = IoBuildDeviceIoControlRequest( code, ext->u.pdo.parent_fdo, NULL, sizeof(params->packet),
511 &params->packet, 0, TRUE, NULL, NULL );
512 break;
515 if (!irp)
517 free( params );
518 return STATUS_NO_MEMORY;
521 IoMarkIrpPending( params->irp );
522 IoSetCompletionRoutine( irp, xfer_completion, params, TRUE, TRUE, TRUE );
523 IoCallDriver( ext->u.pdo.parent_fdo, irp );
524 return STATUS_PENDING;
527 NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
529 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
530 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
531 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
532 NTSTATUS status = irp->IoStatus.Status;
533 ULONG code, index;
534 const WCHAR *str;
535 BOOL removed;
536 KIRQL irql;
538 irp->IoStatus.Information = 0;
540 TRACE( "device %p code %#lx\n", device, irpsp->Parameters.DeviceIoControl.IoControlCode );
542 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
543 removed = ext->u.pdo.removed;
544 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
546 if (removed)
548 irp->IoStatus.Status = STATUS_DELETE_PENDING;
549 IoCompleteRequest(irp, IO_NO_INCREMENT);
550 return STATUS_DELETE_PENDING;
553 switch ((code = irpsp->Parameters.DeviceIoControl.IoControlCode))
555 case IOCTL_HID_GET_POLL_FREQUENCY_MSEC:
556 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
557 status = STATUS_BUFFER_OVERFLOW;
558 else
560 *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval;
561 irp->IoStatus.Information = sizeof(ULONG);
562 status = STATUS_SUCCESS;
564 break;
565 case IOCTL_HID_SET_POLL_FREQUENCY_MSEC:
567 ULONG poll_interval;
568 if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
569 status = STATUS_BUFFER_TOO_SMALL;
570 else
572 poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer;
573 if (poll_interval) ext->u.pdo.poll_interval = min( poll_interval, MAX_POLL_INTERVAL_MSEC );
574 status = STATUS_SUCCESS;
576 break;
578 case IOCTL_HID_GET_PRODUCT_STRING:
579 case IOCTL_HID_GET_SERIALNUMBER_STRING:
580 case IOCTL_HID_GET_MANUFACTURER_STRING:
582 WCHAR *output_buf = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority );
583 ULONG output_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
585 if (code == IOCTL_HID_GET_PRODUCT_STRING) index = HID_STRING_ID_IPRODUCT;
586 if (code == IOCTL_HID_GET_SERIALNUMBER_STRING) index = HID_STRING_ID_ISERIALNUMBER;
587 if (code == IOCTL_HID_GET_MANUFACTURER_STRING) index = HID_STRING_ID_IMANUFACTURER;
589 if ((str = find_device_string( ext->device_id, index )))
591 irp->IoStatus.Information = (wcslen( str ) + 1) * sizeof(WCHAR);
592 if (irp->IoStatus.Information > output_len)
593 status = STATUS_BUFFER_TOO_SMALL;
594 else
596 memcpy( output_buf, str, irp->IoStatus.Information );
597 status = STATUS_SUCCESS;
599 break;
602 call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( index ),
603 sizeof(index), output_buf, output_len, &irp->IoStatus );
604 status = irp->IoStatus.Status;
605 break;
607 case IOCTL_HID_GET_COLLECTION_INFORMATION:
609 irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
610 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
611 status = STATUS_BUFFER_OVERFLOW;
612 else
614 memcpy( irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information,
615 sizeof(HID_COLLECTION_INFORMATION) );
616 status = STATUS_SUCCESS;
618 break;
620 case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
622 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
624 irp->IoStatus.Information = desc->PreparsedDataLength;
625 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength)
626 status = STATUS_INVALID_BUFFER_SIZE;
627 else
629 memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength );
630 status = STATUS_SUCCESS;
632 break;
634 case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
636 if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG))
637 status = STATUS_BUFFER_OVERFLOW;
638 else
639 status = hid_queue_resize( queue, *(ULONG *)irp->AssociatedIrp.SystemBuffer );
640 break;
642 case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
644 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
645 status = STATUS_BUFFER_TOO_SMALL;
646 else
648 *(ULONG *)irp->AssociatedIrp.SystemBuffer = queue->length;
649 irp->IoStatus.Information = sizeof(ULONG);
650 status = STATUS_SUCCESS;
652 break;
654 case IOCTL_HID_GET_FEATURE:
655 case IOCTL_HID_SET_FEATURE:
656 case IOCTL_HID_GET_INPUT_REPORT:
657 case IOCTL_HID_SET_OUTPUT_REPORT:
658 status = hid_device_xfer_report( ext, code, irp );
659 break;
661 case IOCTL_HID_GET_WINE_RAWINPUT_HANDLE:
662 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
663 status = STATUS_BUFFER_OVERFLOW;
664 else
666 *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.rawinput_handle;
667 irp->IoStatus.Information = sizeof(ULONG);
668 status = STATUS_SUCCESS;
670 break;
672 default:
674 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
675 FIXME( "Unsupported ioctl %#lx (device=%lx access=%lx func=%lx method=%lx)\n", code,
676 code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 );
677 status = STATUS_NOT_SUPPORTED;
678 break;
682 if (status != STATUS_PENDING)
684 irp->IoStatus.Status = status;
685 IoCompleteRequest( irp, IO_NO_INCREMENT );
687 return status;
690 NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
692 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
693 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
694 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
695 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
696 struct hid_report *report;
697 BOOL removed;
698 KIRQL irql;
700 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
701 removed = ext->u.pdo.removed;
702 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
704 if (removed)
706 irp->IoStatus.Status = STATUS_DELETE_PENDING;
707 IoCompleteRequest(irp, IO_NO_INCREMENT);
708 return STATUS_DELETE_PENDING;
711 if (irpsp->Parameters.Read.Length < desc->InputLength)
713 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
714 IoCompleteRequest( irp, IO_NO_INCREMENT );
715 return STATUS_INVALID_BUFFER_SIZE;
718 irp->IoStatus.Information = 0;
719 if ((report = hid_queue_pop_report( queue )))
721 memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, report->length );
722 irp->IoStatus.Information = report->length;
723 irp->IoStatus.Status = STATUS_SUCCESS;
724 hid_report_decref( report );
726 IoCompleteRequest( irp, IO_NO_INCREMENT );
727 return STATUS_SUCCESS;
730 return hid_queue_push_irp( queue, irp );
734 NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp)
736 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
737 NTSTATUS status = hid_device_xfer_report( ext, IOCTL_HID_WRITE_REPORT, irp );
738 if (status != STATUS_PENDING)
740 irp->IoStatus.Status = status;
741 IoCompleteRequest( irp, IO_NO_INCREMENT );
743 return status;
746 NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp)
748 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
749 struct hid_queue *queue;
750 BOOL removed;
751 KIRQL irql;
753 TRACE("Open handle on device %p\n", device);
755 KeAcquireSpinLock( &ext->u.pdo.lock, &irql );
756 removed = ext->u.pdo.removed;
757 KeReleaseSpinLock( &ext->u.pdo.lock, irql );
759 if (removed)
761 irp->IoStatus.Status = STATUS_DELETE_PENDING;
762 IoCompleteRequest( irp, IO_NO_INCREMENT );
763 return STATUS_DELETE_PENDING;
766 if (!(queue = hid_queue_create())) irp->IoStatus.Status = STATUS_NO_MEMORY;
767 else
769 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
770 list_add_tail( &ext->u.pdo.queues, &queue->entry );
771 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
773 irp->Tail.Overlay.OriginalFileObject->FsContext = queue;
774 irp->IoStatus.Status = STATUS_SUCCESS;
777 IoCompleteRequest( irp, IO_NO_INCREMENT );
778 return STATUS_SUCCESS;
781 NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp)
783 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
784 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
785 BOOL removed;
786 KIRQL irql;
788 TRACE("Close handle on device %p\n", device);
790 KeAcquireSpinLock( &ext->u.pdo.lock, &irql );
791 removed = ext->u.pdo.removed;
792 KeReleaseSpinLock( &ext->u.pdo.lock, irql );
794 if (removed)
796 irp->IoStatus.Status = STATUS_DELETE_PENDING;
797 IoCompleteRequest( irp, IO_NO_INCREMENT );
798 return STATUS_DELETE_PENDING;
801 if (queue)
803 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
804 list_remove( &queue->entry );
805 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
806 hid_queue_destroy( queue );
809 irp->IoStatus.Status = STATUS_SUCCESS;
810 IoCompleteRequest( irp, IO_NO_INCREMENT );
811 return STATUS_SUCCESS;