hidclass.sys: Validate report IDs in hid_device_xfer_report.
[wine.git] / dlls / hidclass.sys / device.c
blob44965e118a220bbcb4eeee5b5cec347cddbed7b7
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 "wine/debug.h"
28 #include "ddk/hidsdi.h"
29 #include "ddk/hidtypes.h"
30 #include "ddk/wdm.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(hid);
33 WINE_DECLARE_DEBUG_CHANNEL(hid_report);
35 IRP *pop_irp_from_queue(BASE_DEVICE_EXTENSION *ext)
37 LIST_ENTRY *entry;
38 KIRQL old_irql;
39 IRP *irp = NULL;
41 KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
43 while (!irp && (entry = RemoveHeadList(&ext->u.pdo.irp_queue)) != &ext->u.pdo.irp_queue)
45 irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
46 if (!IoSetCancelRoutine(irp, NULL))
48 /* cancel routine is already cleared, meaning that it was called. let it handle completion. */
49 InitializeListHead(&irp->Tail.Overlay.ListEntry);
50 irp = NULL;
54 KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
55 return irp;
58 static void WINAPI read_cancel_routine(DEVICE_OBJECT *device, IRP *irp)
60 BASE_DEVICE_EXTENSION *ext;
61 KIRQL old_irql;
63 TRACE("cancel %p IRP on device %p\n", irp, device);
65 ext = device->DeviceExtension;
67 IoReleaseCancelSpinLock(irp->CancelIrql);
69 KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
71 RemoveEntryList(&irp->Tail.Overlay.ListEntry);
73 KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
75 irp->IoStatus.Status = STATUS_CANCELLED;
76 irp->IoStatus.Information = 0;
77 IoCompleteRequest(irp, IO_NO_INCREMENT);
80 static void hid_device_send_input(DEVICE_OBJECT *device, HID_XFER_PACKET *packet)
82 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
83 RAWINPUT *rawinput;
84 ULONG data_size;
85 INPUT input;
87 data_size = offsetof(RAWINPUT, data.hid.bRawData) + packet->reportBufferLen;
89 if (!(rawinput = malloc(data_size)))
91 ERR("Failed to allocate rawinput data!\n");
92 return;
95 rawinput->header.dwType = RIM_TYPEHID;
96 rawinput->header.dwSize = data_size;
97 rawinput->header.hDevice = ULongToHandle(ext->u.pdo.rawinput_handle);
98 rawinput->header.wParam = RIM_INPUT;
99 rawinput->data.hid.dwCount = 1;
100 rawinput->data.hid.dwSizeHid = data_size - offsetof(RAWINPUT, data.hid.bRawData);
101 memcpy( rawinput->data.hid.bRawData, packet->reportBuffer, packet->reportBufferLen );
103 input.type = INPUT_HARDWARE;
104 input.hi.uMsg = WM_INPUT;
105 input.hi.wParamH = 0;
106 input.hi.wParamL = 0;
107 __wine_send_input(0, &input, rawinput);
109 free(rawinput);
112 static void HID_Device_processQueue(DEVICE_OBJECT *device)
114 IRP *irp;
115 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
116 UINT buffer_size = RingBuffer_GetBufferSize(ext->u.pdo.ring_buffer);
117 const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
118 HID_XFER_PACKET *packet;
120 packet = malloc(buffer_size);
122 while((irp = pop_irp_from_queue(ext)))
124 int ptr = PtrToUlong( irp->Tail.Overlay.OriginalFileObject->FsContext );
126 RingBuffer_Read(ext->u.pdo.ring_buffer, ptr, packet, &buffer_size);
127 if (buffer_size)
129 TRACE_(hid_report)("Processing Request (%i)\n",ptr);
130 memcpy( irp->AssociatedIrp.SystemBuffer, packet + 1, data->caps.InputReportByteLength );
131 irp->IoStatus.Information = packet->reportBufferLen;
132 irp->IoStatus.Status = STATUS_SUCCESS;
134 else
136 irp->IoStatus.Information = 0;
137 irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
139 IoCompleteRequest( irp, IO_NO_INCREMENT );
141 free(packet);
144 static DWORD CALLBACK hid_device_thread(void *args)
146 DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;
147 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
148 const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
149 BYTE report_id = HID_INPUT_VALUE_CAPS( data )->report_id;
150 ULONG buffer_len = data->caps.InputReportByteLength;
151 IO_STATUS_BLOCK io;
152 HID_XFER_PACKET *packet;
153 BYTE *buffer;
154 DWORD rc;
156 packet = malloc( sizeof(*packet) + buffer_len );
157 buffer = (BYTE *)(packet + 1);
158 packet->reportBuffer = buffer;
160 if (ext->u.pdo.information.Polled)
162 while(1)
164 packet->reportId = buffer[0] = report_id;
165 packet->reportBufferLen = buffer_len;
167 if (!report_id)
169 packet->reportBuffer++;
170 packet->reportBufferLen--;
173 call_minidriver( IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, packet,
174 sizeof(*packet), &io );
176 if (io.Status == STATUS_SUCCESS)
178 if (!report_id) io.Information++;
179 packet->reportId = buffer[0];
180 packet->reportBuffer = buffer;
181 packet->reportBufferLen = io.Information;
183 RingBuffer_Write(ext->u.pdo.ring_buffer, packet);
184 hid_device_send_input(device, packet);
185 HID_Device_processQueue(device);
188 rc = WaitForSingleObject(ext->u.pdo.halt_event,
189 ext->u.pdo.poll_interval ? ext->u.pdo.poll_interval : DEFAULT_POLL_INTERVAL);
191 if (rc == WAIT_OBJECT_0)
192 break;
193 else if (rc != WAIT_TIMEOUT)
194 ERR("Wait returned unexpected value %x\n",rc);
197 else
199 INT exit_now = FALSE;
201 while(1)
203 packet->reportId = buffer[0] = report_id;
204 packet->reportBufferLen = buffer_len;
206 if (!report_id)
208 packet->reportBuffer++;
209 packet->reportBufferLen--;
212 call_minidriver( IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo, NULL, 0,
213 packet->reportBuffer, packet->reportBufferLen, &io );
215 rc = WaitForSingleObject(ext->u.pdo.halt_event, 0);
216 if (rc == WAIT_OBJECT_0)
217 exit_now = TRUE;
219 if (!exit_now && io.Status == STATUS_SUCCESS)
221 if (!report_id) io.Information++;
222 packet->reportId = buffer[0];
223 packet->reportBuffer = buffer;
224 packet->reportBufferLen = io.Information;
226 RingBuffer_Write(ext->u.pdo.ring_buffer, packet);
227 hid_device_send_input(device, packet);
228 HID_Device_processQueue(device);
231 if (exit_now)
232 break;
236 TRACE("Device thread exiting\n");
237 return 1;
240 void HID_StartDeviceThread(DEVICE_OBJECT *device)
242 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
243 ext->u.pdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL);
244 ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL);
247 static void handle_IOCTL_HID_GET_COLLECTION_INFORMATION( IRP *irp, BASE_DEVICE_EXTENSION *ext )
249 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
250 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_COLLECTION_INFORMATION))
252 irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
253 irp->IoStatus.Information = 0;
255 else
257 memcpy(irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information, sizeof(HID_COLLECTION_INFORMATION));
258 irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
259 irp->IoStatus.Status = STATUS_SUCCESS;
263 static void handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( IRP *irp, BASE_DEVICE_EXTENSION *ext )
265 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
266 const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
268 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < data->dwSize)
270 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
271 irp->IoStatus.Information = 0;
273 else
275 memcpy(irp->UserBuffer, data, data->dwSize);
276 irp->IoStatus.Information = data->dwSize;
277 irp->IoStatus.Status = STATUS_SUCCESS;
281 static void handle_minidriver_string( BASE_DEVICE_EXTENSION *ext, IRP *irp, SHORT index )
283 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
284 WCHAR buffer[127];
285 ULONG InputBuffer;
287 InputBuffer = MAKELONG(index, 0);
289 call_minidriver( IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo, ULongToPtr( InputBuffer ),
290 sizeof(InputBuffer), buffer, sizeof(buffer), &irp->IoStatus );
292 if (irp->IoStatus.Status == STATUS_SUCCESS)
294 WCHAR *out_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
295 int length = irpsp->Parameters.DeviceIoControl.OutputBufferLength/sizeof(WCHAR);
296 TRACE("got string %s from minidriver\n",debugstr_w(buffer));
297 lstrcpynW(out_buffer, buffer, length);
298 irp->IoStatus.Information = (lstrlenW(buffer)+1) * sizeof(WCHAR);
302 static void hid_device_xfer_report( BASE_DEVICE_EXTENSION *ext, ULONG code, IRP *irp )
304 const WINE_HIDP_PREPARSED_DATA *preparsed = ext->u.pdo.preparsed_data;
305 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
306 struct hid_value_caps *caps = NULL, *caps_end = NULL;
307 ULONG report_len = 0, buffer_len = 0;
308 HID_XFER_PACKET packet;
309 BYTE *buffer = NULL;
311 switch (code)
313 case IOCTL_HID_GET_FEATURE:
314 case IOCTL_HID_GET_INPUT_REPORT:
315 buffer_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
316 buffer = MmGetSystemAddressForMdlSafe( irp->MdlAddress, NormalPagePriority );
317 break;
318 case IOCTL_HID_SET_FEATURE:
319 case IOCTL_HID_SET_OUTPUT_REPORT:
320 buffer_len = stack->Parameters.DeviceIoControl.InputBufferLength;
321 buffer = irp->AssociatedIrp.SystemBuffer;
322 break;
325 switch (code)
327 case IOCTL_HID_GET_INPUT_REPORT:
328 report_len = preparsed->caps.InputReportByteLength;
329 caps = HID_INPUT_VALUE_CAPS( preparsed );
330 caps_end = caps + preparsed->value_caps_count[HidP_Input];
331 break;
332 case IOCTL_HID_SET_OUTPUT_REPORT:
333 report_len = preparsed->caps.OutputReportByteLength;
334 caps = HID_OUTPUT_VALUE_CAPS( preparsed );
335 caps_end = caps + preparsed->value_caps_count[HidP_Output];
336 break;
337 case IOCTL_HID_GET_FEATURE:
338 case IOCTL_HID_SET_FEATURE:
339 report_len = preparsed->caps.FeatureReportByteLength;
340 caps = HID_FEATURE_VALUE_CAPS( preparsed );
341 caps_end = caps + preparsed->value_caps_count[HidP_Feature];
342 break;
345 if (!buffer || !buffer_len)
347 irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
348 return;
350 if (buffer_len < report_len)
352 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
353 return;
356 for (; caps != caps_end; ++caps) if (!caps->report_id || caps->report_id == buffer[0]) break;
357 if (caps == caps_end)
359 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
360 return;
363 packet.reportId = buffer[0];
364 packet.reportBuffer = buffer;
365 packet.reportBufferLen = buffer_len;
367 if (!caps->report_id)
369 packet.reportId = 0;
370 packet.reportBuffer++;
371 packet.reportBufferLen--;
374 switch (code)
376 case IOCTL_HID_GET_FEATURE:
377 case IOCTL_HID_GET_INPUT_REPORT:
378 call_minidriver( code, ext->u.pdo.parent_fdo, NULL, 0, &packet, sizeof(packet), &irp->IoStatus );
379 break;
380 case IOCTL_HID_SET_FEATURE:
381 case IOCTL_HID_SET_OUTPUT_REPORT:
382 call_minidriver( code, ext->u.pdo.parent_fdo, NULL, sizeof(packet), &packet, 0, &irp->IoStatus );
383 break;
387 NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
389 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
390 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
391 NTSTATUS status;
392 BOOL removed;
393 ULONG code;
394 KIRQL irql;
396 irp->IoStatus.Information = 0;
398 TRACE("device %p ioctl(%x)\n", device, irpsp->Parameters.DeviceIoControl.IoControlCode);
400 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
401 removed = ext->u.pdo.removed;
402 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
404 if (removed)
406 irp->IoStatus.Status = STATUS_DELETE_PENDING;
407 IoCompleteRequest(irp, IO_NO_INCREMENT);
408 return STATUS_DELETE_PENDING;
411 switch ((code = irpsp->Parameters.DeviceIoControl.IoControlCode))
413 case IOCTL_HID_GET_POLL_FREQUENCY_MSEC:
414 TRACE("IOCTL_HID_GET_POLL_FREQUENCY_MSEC\n");
415 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
417 irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
418 irp->IoStatus.Information = 0;
419 break;
421 *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval;
422 irp->IoStatus.Information = sizeof(ULONG);
423 irp->IoStatus.Status = STATUS_SUCCESS;
424 break;
425 case IOCTL_HID_SET_POLL_FREQUENCY_MSEC:
427 ULONG poll_interval;
428 TRACE("IOCTL_HID_SET_POLL_FREQUENCY_MSEC\n");
429 if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
431 irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
432 break;
434 poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer;
435 if (poll_interval <= MAX_POLL_INTERVAL_MSEC)
437 ext->u.pdo.poll_interval = poll_interval;
438 irp->IoStatus.Status = STATUS_SUCCESS;
440 else
441 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
442 break;
444 case IOCTL_HID_GET_PRODUCT_STRING:
446 handle_minidriver_string( ext, irp, HID_STRING_ID_IPRODUCT );
447 break;
449 case IOCTL_HID_GET_SERIALNUMBER_STRING:
451 handle_minidriver_string( ext, irp, HID_STRING_ID_ISERIALNUMBER );
452 break;
454 case IOCTL_HID_GET_MANUFACTURER_STRING:
456 handle_minidriver_string( ext, irp, HID_STRING_ID_IMANUFACTURER );
457 break;
459 case IOCTL_HID_GET_COLLECTION_INFORMATION:
461 handle_IOCTL_HID_GET_COLLECTION_INFORMATION( irp, ext );
462 break;
464 case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
466 handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR( irp, ext );
467 break;
469 case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
471 irp->IoStatus.Information = 0;
473 if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG))
474 irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
475 else
476 irp->IoStatus.Status = RingBuffer_SetSize( ext->u.pdo.ring_buffer, *(ULONG *)irp->AssociatedIrp.SystemBuffer );
477 break;
479 case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
481 if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
483 irp->IoStatus.Information = 0;
484 irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
486 else
488 *(ULONG *)irp->AssociatedIrp.SystemBuffer = RingBuffer_GetSize(ext->u.pdo.ring_buffer);
489 irp->IoStatus.Information = sizeof(ULONG);
490 irp->IoStatus.Status = STATUS_SUCCESS;
492 break;
494 case IOCTL_HID_GET_FEATURE:
495 case IOCTL_HID_SET_FEATURE:
496 case IOCTL_HID_GET_INPUT_REPORT:
497 case IOCTL_HID_SET_OUTPUT_REPORT:
498 hid_device_xfer_report( ext, code, irp );
499 break;
500 default:
502 ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
503 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
504 code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
505 irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
506 break;
510 status = irp->IoStatus.Status;
511 if (status != STATUS_PENDING) IoCompleteRequest( irp, IO_NO_INCREMENT );
512 return status;
515 NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
517 HID_XFER_PACKET *packet;
518 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
519 const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
520 UINT buffer_size = RingBuffer_GetBufferSize(ext->u.pdo.ring_buffer);
521 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
522 BYTE report_id = HID_INPUT_VALUE_CAPS( data )->report_id;
523 NTSTATUS status;
524 int ptr = -1;
525 BOOL removed;
526 KIRQL irql;
528 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
529 removed = ext->u.pdo.removed;
530 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
532 if (removed)
534 irp->IoStatus.Status = STATUS_DELETE_PENDING;
535 IoCompleteRequest(irp, IO_NO_INCREMENT);
536 return STATUS_DELETE_PENDING;
539 if (irpsp->Parameters.Read.Length < data->caps.InputReportByteLength)
541 irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
542 IoCompleteRequest( irp, IO_NO_INCREMENT );
543 return STATUS_INVALID_BUFFER_SIZE;
546 packet = malloc(buffer_size);
547 ptr = PtrToUlong( irp->Tail.Overlay.OriginalFileObject->FsContext );
549 irp->IoStatus.Information = 0;
550 RingBuffer_ReadNew(ext->u.pdo.ring_buffer, ptr, packet, &buffer_size);
552 if (buffer_size)
554 memcpy( irp->AssociatedIrp.SystemBuffer, packet + 1, data->caps.InputReportByteLength );
555 irp->IoStatus.Information = packet->reportBufferLen;
556 irp->IoStatus.Status = STATUS_SUCCESS;
558 else
560 if (ext->u.pdo.poll_interval)
562 KIRQL old_irql;
563 TRACE_(hid_report)("Queue irp\n");
565 KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
567 IoSetCancelRoutine(irp, read_cancel_routine);
568 if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
570 /* IRP was canceled before we set cancel routine */
571 InitializeListHead(&irp->Tail.Overlay.ListEntry);
572 KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
573 return STATUS_CANCELLED;
576 InsertTailList(&ext->u.pdo.irp_queue, &irp->Tail.Overlay.ListEntry);
577 irp->IoStatus.Status = STATUS_PENDING;
578 IoMarkIrpPending(irp);
580 KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
582 else
584 HID_XFER_PACKET packet;
585 BYTE *buffer = irp->AssociatedIrp.SystemBuffer;
586 ULONG buffer_len = irpsp->Parameters.Read.Length;
588 TRACE("No packet, but opportunistic reads enabled\n");
590 packet.reportId = buffer[0];
591 packet.reportBuffer = buffer;
592 packet.reportBufferLen = buffer_len;
594 if (!report_id)
596 packet.reportId = 0;
597 packet.reportBuffer++;
598 packet.reportBufferLen--;
601 call_minidriver( IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, &packet,
602 sizeof(packet), &irp->IoStatus );
605 free(packet);
607 status = irp->IoStatus.Status;
608 if (status != STATUS_PENDING) IoCompleteRequest( irp, IO_NO_INCREMENT );
609 return status;
612 NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp)
614 IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
615 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
616 const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
617 HID_XFER_PACKET packet;
618 NTSTATUS status;
619 ULONG max_len;
620 BOOL removed;
621 KIRQL irql;
623 KeAcquireSpinLock(&ext->u.pdo.lock, &irql);
624 removed = ext->u.pdo.removed;
625 KeReleaseSpinLock(&ext->u.pdo.lock, irql);
627 if (removed)
629 irp->IoStatus.Status = STATUS_DELETE_PENDING;
630 IoCompleteRequest(irp, IO_NO_INCREMENT);
631 return STATUS_DELETE_PENDING;
634 if (!irpsp->Parameters.Write.Length)
636 irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
637 IoCompleteRequest( irp, IO_NO_INCREMENT );
638 return STATUS_INVALID_USER_BUFFER;
641 if (irpsp->Parameters.Write.Length < data->caps.OutputReportByteLength)
643 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
644 IoCompleteRequest( irp, IO_NO_INCREMENT );
645 return STATUS_INVALID_PARAMETER;
648 irp->IoStatus.Information = 0;
650 TRACE_(hid_report)("Device %p Buffer length %i Buffer %p\n", device, irpsp->Parameters.Write.Length, irp->AssociatedIrp.SystemBuffer);
651 packet.reportId = ((BYTE*)irp->AssociatedIrp.SystemBuffer)[0];
652 if (packet.reportId == 0)
654 packet.reportBuffer = &((BYTE*)irp->AssociatedIrp.SystemBuffer)[1];
655 packet.reportBufferLen = irpsp->Parameters.Write.Length - 1;
656 max_len = data->caps.OutputReportByteLength;
658 else
660 packet.reportBuffer = irp->AssociatedIrp.SystemBuffer;
661 packet.reportBufferLen = irpsp->Parameters.Write.Length;
662 max_len = (data->reports[data->reportIdx[HidP_Output][packet.reportId]].bitSize + 7) / 8;
664 if (packet.reportBufferLen > max_len)
665 packet.reportBufferLen = max_len;
667 TRACE_(hid_report)("(id %i, len %i buffer %p)\n", packet.reportId, packet.reportBufferLen, packet.reportBuffer);
669 call_minidriver( IOCTL_HID_WRITE_REPORT, ext->u.pdo.parent_fdo, NULL, 0, &packet,
670 sizeof(packet), &irp->IoStatus );
672 if (irp->IoStatus.Status == STATUS_SUCCESS)
673 irp->IoStatus.Information = irpsp->Parameters.Write.Length;
674 else
675 irp->IoStatus.Information = 0;
677 TRACE_(hid_report)( "Result 0x%x wrote %li bytes\n", irp->IoStatus.Status, irp->IoStatus.Information );
679 status = irp->IoStatus.Status;
680 IoCompleteRequest( irp, IO_NO_INCREMENT );
681 return status;
684 NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp)
686 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
688 TRACE("Open handle on device %p\n", device);
689 irp->Tail.Overlay.OriginalFileObject->FsContext = UlongToPtr(RingBuffer_AddPointer(ext->u.pdo.ring_buffer));
690 irp->IoStatus.Status = STATUS_SUCCESS;
691 IoCompleteRequest( irp, IO_NO_INCREMENT );
692 return STATUS_SUCCESS;
695 NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp)
697 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
698 int ptr = PtrToUlong(irp->Tail.Overlay.OriginalFileObject->FsContext);
699 TRACE("Close handle on device %p\n", device);
700 RingBuffer_RemovePointer(ext->u.pdo.ring_buffer, ptr);
701 irp->IoStatus.Status = STATUS_SUCCESS;
702 IoCompleteRequest( irp, IO_NO_INCREMENT );
703 return STATUS_SUCCESS;