Release 9.12.
[wine.git] / dlls / hidclass.sys / device.c
blobe6b34077a776ec8d90f73bdede35c3f5f958dd83
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 "ntuser.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 KIRQL irql;
229 IRP *irp;
231 TRACE("device %p, packet %p\n", device, packet);
233 if (IsEqualGUID( ext->class_guid, &GUID_DEVINTERFACE_HID ))
235 struct hid_packet *hid;
237 size = offsetof( struct hid_packet, data[report_len] );
238 if (!(hid = malloc( size ))) ERR( "Failed to allocate rawinput data!\n" );
239 else
241 INPUT input = {.type = INPUT_HARDWARE};
243 input.hi.uMsg = WM_INPUT;
244 input.hi.wParamH = HIWORD(RIM_INPUT);
245 input.hi.wParamL = LOWORD(RIM_INPUT);
247 hid->head.device = ext->u.pdo.rawinput_handle;
248 hid->head.usage = MAKELONG(desc->Usage, desc->UsagePage);
250 hid->head.count = 1;
251 hid->head.length = report_len;
252 memcpy( hid->data, packet->reportBuffer, packet->reportBufferLen );
253 memset( hid->data + packet->reportBufferLen, 0, report_len - packet->reportBufferLen );
254 NtUserSendHardwareInput( 0, 0, &input, (LPARAM)hid );
256 free( hid );
260 if (!(last_report = hid_report_create( packet, report_len )))
262 ERR( "Failed to allocate hid_report!\n" );
263 return;
266 InitializeListHead( &completed );
268 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
269 LIST_FOR_EACH_ENTRY( queue, &ext->u.pdo.queues, struct hid_queue, entry )
271 if (!polled) hid_queue_push_report( queue, last_report );
275 if (!(irp = hid_queue_pop_irp( queue ))) break;
276 if (!(report = hid_queue_pop_report( queue ))) hid_report_incref( (report = last_report) );
278 memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, report->length );
279 irp->IoStatus.Information = report->length;
280 irp->IoStatus.Status = STATUS_SUCCESS;
281 hid_report_decref( report );
283 InsertTailList( &completed, &irp->Tail.Overlay.ListEntry );
285 while (polled);
287 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
289 while ((entry = RemoveHeadList( &completed )) != &completed)
291 irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry );
292 IoCompleteRequest( irp, IO_NO_INCREMENT );
295 hid_report_decref( last_report );
298 static HIDP_REPORT_IDS *find_report_with_type_and_id( BASE_DEVICE_EXTENSION *ext, BYTE type, BYTE id, BOOL any_id )
300 HIDP_REPORT_IDS *report, *reports = ext->u.pdo.device_desc.ReportIDs;
301 ULONG report_count = ext->u.pdo.device_desc.ReportIDsLength;
303 for (report = reports; report != reports + report_count; report++)
305 if (!any_id && report->ReportID && report->ReportID != id) continue;
306 if (type == HidP_Input && report->InputLength) return report;
307 if (type == HidP_Output && report->OutputLength) return report;
308 if (type == HidP_Feature && report->FeatureLength) return report;
311 return NULL;
314 static DWORD CALLBACK hid_device_thread(void *args)
316 DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
317 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
318 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
319 BOOL polled = ext->u.pdo.information.Polled;
320 HIDP_REPORT_IDS *report;
321 HID_XFER_PACKET *packet;
322 ULONG report_id = 0;
323 IO_STATUS_BLOCK io;
324 BYTE *buffer;
325 DWORD res;
327 packet = malloc( sizeof(*packet) + desc->InputLength );
328 buffer = (BYTE *)(packet + 1);
330 report = find_report_with_type_and_id( ext, HidP_Input, 0, TRUE );
331 if (!report) WARN("no input report found.\n");
332 else report_id = report->ReportID;
336 packet->reportId = buffer[0] = report_id;
337 packet->reportBuffer = buffer;
338 packet->reportBufferLen = desc->InputLength;
340 if (!report_id)
342 packet->reportBuffer++;
343 packet->reportBufferLen--;
346 call_minidriver( IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo, NULL, 0,
347 packet->reportBuffer, packet->reportBufferLen, &io );
349 if (io.Status == STATUS_SUCCESS)
351 if (!report_id) io.Information++;
352 if (!(report = find_report_with_type_and_id( ext, HidP_Input, buffer[0], FALSE )))
353 WARN( "dropping unknown input id %u\n", buffer[0] );
354 else if (!polled && io.Information < report->InputLength)
355 WARN( "dropping short report, len %Iu expected %u\n", io.Information, report->InputLength );
356 else
358 packet->reportId = buffer[0];
359 packet->reportBuffer = buffer;
360 packet->reportBufferLen = io.Information;
361 hid_device_queue_input( device, packet );
365 res = WaitForSingleObject(ext->u.pdo.halt_event, polled ? ext->u.pdo.poll_interval : 0);
366 } while (res == WAIT_TIMEOUT);
368 TRACE( "device thread exiting, res %#lx\n", res );
369 return 1;
372 void HID_StartDeviceThread(DEVICE_OBJECT *device)
374 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
375 ext->u.pdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL);
376 ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL);
379 struct device_strings
381 const WCHAR *id;
382 const WCHAR *product;
385 static const struct device_strings device_strings[] =
387 /* Microsoft controllers */
388 { .id = L"VID_045E&PID_028E", .product = L"Controller (XBOX 360 For Windows)" },
389 { .id = L"VID_045E&PID_028F", .product = L"Controller (XBOX 360 For Windows)" },
390 { .id = L"VID_045E&PID_02D1", .product = L"Controller (Xbox One For Windows)" },
391 { .id = L"VID_045E&PID_02DD", .product = L"Controller (Xbox One For Windows)" },
392 { .id = L"VID_045E&PID_02E3", .product = L"Controller (Xbox One For Windows)" },
393 { .id = L"VID_045E&PID_02EA", .product = L"Controller (Xbox One For Windows)" },
394 { .id = L"VID_045E&PID_02FD", .product = L"Controller (Xbox One For Windows)" },
395 { .id = L"VID_045E&PID_0719", .product = L"Controller (XBOX 360 For Windows)" },
396 { .id = L"VID_045E&PID_0B00", .product = L"Controller (Xbox One For Windows)" },
397 { .id = L"VID_045E&PID_0B05", .product = L"Controller (Xbox One For Windows)" },
398 { .id = L"VID_045E&PID_0B12", .product = L"Controller (Xbox One For Windows)" },
399 { .id = L"VID_045E&PID_0B13", .product = L"Controller (Xbox One For Windows)" },
400 /* Sony controllers */
401 { .id = L"VID_054C&PID_05C4", .product = L"Wireless Controller" },
402 { .id = L"VID_054C&PID_09CC", .product = L"Wireless Controller" },
403 { .id = L"VID_054C&PID_0BA0", .product = L"Wireless Controller" },
404 { .id = L"VID_054C&PID_0CE6", .product = L"Wireless Controller" },
405 { .id = L"VID_054C&PID_0DF2", .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 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
530 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
531 NTSTATUS status = irp->IoStatus.Status;
532 ULONG code, index;
533 const WCHAR *str;
534 BOOL removed;
535 KIRQL irql;
537 irp->IoStatus.Information = 0;
539 TRACE( "device %p code %#lx\n", device, irpsp->Parameters.DeviceIoControl.IoControlCode );
541 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
542 removed = ext->u.pdo.removed;
543 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
545 if (removed)
547 irp->IoStatus.Status = STATUS_DELETE_PENDING;
548 IoCompleteRequest(irp, IO_NO_INCREMENT);
549 return STATUS_DELETE_PENDING;
552 switch ((code = irpsp->Parameters.DeviceIoControl.IoControlCode))
554 case IOCTL_HID_GET_POLL_FREQUENCY_MSEC:
555 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
556 status = STATUS_BUFFER_OVERFLOW;
557 else
559 *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval;
560 irp->IoStatus.Information = sizeof(ULONG);
561 status = STATUS_SUCCESS;
563 break;
564 case IOCTL_HID_SET_POLL_FREQUENCY_MSEC:
566 ULONG poll_interval;
567 if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
568 status = STATUS_BUFFER_TOO_SMALL;
569 else
571 poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer;
572 if (poll_interval) ext->u.pdo.poll_interval = min( poll_interval, MAX_POLL_INTERVAL_MSEC );
573 status = STATUS_SUCCESS;
575 break;
577 case IOCTL_HID_GET_PRODUCT_STRING:
578 case IOCTL_HID_GET_SERIALNUMBER_STRING:
579 case IOCTL_HID_GET_MANUFACTURER_STRING:
581 WCHAR *output_buf = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority );
582 ULONG output_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
584 if (code == IOCTL_HID_GET_PRODUCT_STRING) index = HID_STRING_ID_IPRODUCT;
585 if (code == IOCTL_HID_GET_SERIALNUMBER_STRING) index = HID_STRING_ID_ISERIALNUMBER;
586 if (code == IOCTL_HID_GET_MANUFACTURER_STRING) index = HID_STRING_ID_IMANUFACTURER;
588 if ((str = find_device_string( ext->device_id, index )))
590 irp->IoStatus.Information = (wcslen( str ) + 1) * sizeof(WCHAR);
591 if (irp->IoStatus.Information > output_len)
592 status = STATUS_BUFFER_TOO_SMALL;
593 else
595 memcpy( output_buf, str, irp->IoStatus.Information );
596 status = STATUS_SUCCESS;
598 break;
601 call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( index ),
602 sizeof(index), output_buf, output_len, &irp->IoStatus );
603 status = irp->IoStatus.Status;
604 break;
606 case IOCTL_HID_GET_COLLECTION_INFORMATION:
608 irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
609 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
610 status = STATUS_BUFFER_OVERFLOW;
611 else
613 memcpy( irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information,
614 sizeof(HID_COLLECTION_INFORMATION) );
615 status = STATUS_SUCCESS;
617 break;
619 case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
621 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
623 irp->IoStatus.Information = desc->PreparsedDataLength;
624 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < desc->PreparsedDataLength)
625 status = STATUS_INVALID_BUFFER_SIZE;
626 else
628 memcpy( irp->UserBuffer, desc->PreparsedData, desc->PreparsedDataLength );
629 status = STATUS_SUCCESS;
631 break;
633 case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
635 if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG))
636 status = STATUS_BUFFER_OVERFLOW;
637 else
639 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
640 status = hid_queue_resize( queue, *(ULONG *)irp->AssociatedIrp.SystemBuffer );
642 break;
644 case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
646 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
647 status = STATUS_BUFFER_TOO_SMALL;
648 else
650 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
651 *(ULONG *)irp->AssociatedIrp.SystemBuffer = queue->length;
652 irp->IoStatus.Information = sizeof(ULONG);
653 status = STATUS_SUCCESS;
655 break;
657 case IOCTL_HID_GET_FEATURE:
658 case IOCTL_HID_SET_FEATURE:
659 case IOCTL_HID_GET_INPUT_REPORT:
660 case IOCTL_HID_SET_OUTPUT_REPORT:
661 status = hid_device_xfer_report( ext, code, irp );
662 break;
664 case IOCTL_HID_GET_WINE_RAWINPUT_HANDLE:
665 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
666 status = STATUS_BUFFER_OVERFLOW;
667 else
669 *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.rawinput_handle;
670 irp->IoStatus.Information = sizeof(ULONG);
671 status = STATUS_SUCCESS;
673 break;
675 default:
677 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
678 FIXME( "Unsupported ioctl %#lx (device=%lx access=%lx func=%lx method=%lx)\n", code,
679 code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 );
680 status = STATUS_NOT_SUPPORTED;
681 break;
685 if (status != STATUS_PENDING)
687 irp->IoStatus.Status = status;
688 IoCompleteRequest( irp, IO_NO_INCREMENT );
690 return status;
693 NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
695 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
696 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
697 HIDP_COLLECTION_DESC *desc = ext->u.pdo.device_desc.CollectionDesc;
698 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
699 struct hid_report *report;
700 BOOL removed;
701 KIRQL irql;
703 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
704 removed = ext->u.pdo.removed;
705 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
707 if (removed)
709 irp->IoStatus.Status = STATUS_DELETE_PENDING;
710 IoCompleteRequest(irp, IO_NO_INCREMENT);
711 return STATUS_DELETE_PENDING;
714 if (irpsp->Parameters.Read.Length < desc->InputLength)
716 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
717 IoCompleteRequest( irp, IO_NO_INCREMENT );
718 return STATUS_INVALID_BUFFER_SIZE;
721 irp->IoStatus.Information = 0;
722 if ((report = hid_queue_pop_report( queue )))
724 memcpy( irp->AssociatedIrp.SystemBuffer, report->buffer, report->length );
725 irp->IoStatus.Information = report->length;
726 irp->IoStatus.Status = STATUS_SUCCESS;
727 hid_report_decref( report );
729 IoCompleteRequest( irp, IO_NO_INCREMENT );
730 return STATUS_SUCCESS;
733 return hid_queue_push_irp( queue, irp );
737 NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp)
739 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
740 NTSTATUS status = hid_device_xfer_report( ext, IOCTL_HID_WRITE_REPORT, irp );
741 if (status != STATUS_PENDING)
743 irp->IoStatus.Status = status;
744 IoCompleteRequest( irp, IO_NO_INCREMENT );
746 return status;
749 NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp)
751 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
752 struct hid_queue *queue;
753 BOOL removed;
754 KIRQL irql;
756 TRACE("Open handle on device %p\n", device);
758 KeAcquireSpinLock( &ext->u.pdo.lock, &irql );
759 removed = ext->u.pdo.removed;
760 KeReleaseSpinLock( &ext->u.pdo.lock, irql );
762 if (removed)
764 irp->IoStatus.Status = STATUS_DELETE_PENDING;
765 IoCompleteRequest( irp, IO_NO_INCREMENT );
766 return STATUS_DELETE_PENDING;
769 if (!(queue = hid_queue_create())) irp->IoStatus.Status = STATUS_NO_MEMORY;
770 else
772 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
773 list_add_tail( &ext->u.pdo.queues, &queue->entry );
774 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
776 irp->Tail.Overlay.OriginalFileObject->FsContext = queue;
777 irp->IoStatus.Status = STATUS_SUCCESS;
780 IoCompleteRequest( irp, IO_NO_INCREMENT );
781 return STATUS_SUCCESS;
784 NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp)
786 struct hid_queue *queue = irp->Tail.Overlay.OriginalFileObject->FsContext;
787 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
788 BOOL removed;
789 KIRQL irql;
791 TRACE("Close handle on device %p\n", device);
793 KeAcquireSpinLock( &ext->u.pdo.lock, &irql );
794 removed = ext->u.pdo.removed;
795 KeReleaseSpinLock( &ext->u.pdo.lock, irql );
797 if (removed)
799 irp->IoStatus.Status = STATUS_DELETE_PENDING;
800 IoCompleteRequest( irp, IO_NO_INCREMENT );
801 return STATUS_DELETE_PENDING;
804 if (queue)
806 KeAcquireSpinLock( &ext->u.pdo.queues_lock, &irql );
807 list_remove( &queue->entry );
808 KeReleaseSpinLock( &ext->u.pdo.queues_lock, irql );
809 hid_queue_destroy( queue );
812 irp->IoStatus.Status = STATUS_SUCCESS;
813 IoCompleteRequest( irp, IO_NO_INCREMENT );
814 return STATUS_SUCCESS;