device.c 23.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * HIDClass device functions
 *
 * Copyright (C) 2015 Aric Stewart
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#define NONAMELESSUNION
23
#define NONAMELESSSTRUCT
24
#include "initguid.h"
25 26 27 28 29 30 31
#include "hid.h"
#include "winreg.h"
#include "winuser.h"

#include "wine/debug.h"
#include "ddk/hidsdi.h"
#include "ddk/hidtypes.h"
32
#include "ddk/wdm.h"
33
#include "devguid.h"
34
#include "ntddmou.h"
35 36

WINE_DEFAULT_DEBUG_CHANNEL(hid);
37
WINE_DECLARE_DEBUG_CHANNEL(hid_report);
38

39
IRP *pop_irp_from_queue(BASE_DEVICE_EXTENSION *ext)
40 41 42 43 44
{
    LIST_ENTRY *entry;
    KIRQL old_irql;
    IRP *irp = NULL;

45
    KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
46

47
    while (!irp && (entry = RemoveHeadList(&ext->u.pdo.irp_queue)) != &ext->u.pdo.irp_queue)
48
    {
49
        irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.s.ListEntry);
50 51 52 53 54 55 56
        if (!IoSetCancelRoutine(irp, NULL))
        {
            /* cancel routine is already cleared, meaning that it was called. let it handle completion. */
            InitializeListHead(&irp->Tail.Overlay.s.ListEntry);
            irp = NULL;
        }
    }
57

58
    KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
59 60 61
    return irp;
}

62 63 64 65 66 67 68 69 70 71 72
static void WINAPI read_cancel_routine(DEVICE_OBJECT *device, IRP *irp)
{
    BASE_DEVICE_EXTENSION *ext;
    KIRQL old_irql;

    TRACE("cancel %p IRP on device %p\n", irp, device);

    ext = device->DeviceExtension;

    IoReleaseCancelSpinLock(irp->CancelIrql);

73
    KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
74 75 76

    RemoveEntryList(&irp->Tail.Overlay.s.ListEntry);

77
    KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
78 79 80 81 82 83

    irp->IoStatus.u.Status = STATUS_CANCELLED;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static NTSTATUS copy_packet_into_buffer(HID_XFER_PACKET *packet, BYTE* buffer, ULONG buffer_length, ULONG *out_length)
{
    BOOL zero_id = (packet->reportId == 0);

    *out_length = 0;

    if ((zero_id && buffer_length > packet->reportBufferLen) ||
        (!zero_id && buffer_length >= packet->reportBufferLen))
    {
        if (packet->reportId != 0)
        {
            memcpy(buffer, packet->reportBuffer, packet->reportBufferLen);
            *out_length = packet->reportBufferLen;
        }
        else
        {
            buffer[0] = 0;
            memcpy(&buffer[1], packet->reportBuffer, packet->reportBufferLen);
            *out_length = packet->reportBufferLen + 1;
        }
        return STATUS_SUCCESS;
    }
    else
        return STATUS_BUFFER_OVERFLOW;
}

110 111 112 113
static void HID_Device_processQueue(DEVICE_OBJECT *device)
{
    IRP *irp;
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
114
    UINT buffer_size = RingBuffer_GetBufferSize(ext->u.pdo.ring_buffer);
115 116 117 118
    HID_XFER_PACKET *packet;

    packet = HeapAlloc(GetProcessHeap(), 0, buffer_size);

119
    while((irp = pop_irp_from_queue(ext)))
120 121 122 123
    {
        int ptr;
        ptr = PtrToUlong( irp->Tail.Overlay.OriginalFileObject->FsContext );

124
        RingBuffer_Read(ext->u.pdo.ring_buffer, ptr, packet, &buffer_size);
125 126
        if (buffer_size)
        {
127 128
            NTSTATUS rc;
            ULONG out_length;
129
            IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
130
            packet->reportBuffer = (BYTE *)packet + sizeof(*packet);
131
            TRACE_(hid_report)("Processing Request (%i)\n",ptr);
132 133 134
            rc = copy_packet_into_buffer(packet, irp->AssociatedIrp.SystemBuffer, irpsp->Parameters.Read.Length, &out_length);
            irp->IoStatus.u.Status = rc;
            irp->IoStatus.Information = out_length;
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
        }
        else
        {
            irp->IoStatus.Information = 0;
            irp->IoStatus.u.Status = STATUS_UNSUCCESSFUL;
        }
        IoCompleteRequest( irp, IO_NO_INCREMENT );
    }
    HeapFree(GetProcessHeap(), 0, packet);
}

static DWORD CALLBACK hid_device_thread(void *args)
{
    DEVICE_OBJECT *device = (DEVICE_OBJECT*)args;

    IRP *irp;
    IO_STATUS_BLOCK irp_status;
152
    HID_XFER_PACKET *packet;
153 154 155
    DWORD rc;

    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
156
    USHORT report_size = ext->u.pdo.preparsed_data->caps.InputReportByteLength;
157

158
    packet = HeapAlloc(GetProcessHeap(), 0, sizeof(*packet) + report_size);
159 160
    packet->reportBuffer = (BYTE *)packet + sizeof(*packet);

161
    if (ext->u.pdo.information.Polled)
162 163 164
    {
        while(1)
        {
165 166 167
            KEVENT event;

            KeInitializeEvent(&event, NotificationEvent, FALSE);
168

169
            packet->reportBufferLen = report_size;
170 171
            packet->reportId = 0;

172
            irp = IoBuildDeviceIoControlRequest(IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo,
173
                    NULL, 0, packet, sizeof(*packet), TRUE, &event, &irp_status);
174

175
            if (IoCallDriver(ext->u.pdo.parent_fdo, irp) == STATUS_PENDING)
176
                KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
177

178
            if (irp_status.u.Status == STATUS_SUCCESS)
179
            {
180
                RingBuffer_Write(ext->u.pdo.ring_buffer, packet);
181 182 183
                HID_Device_processQueue(device);
            }

184 185
            rc = WaitForSingleObject(ext->u.pdo.halt_event,
                    ext->u.pdo.poll_interval ? ext->u.pdo.poll_interval : DEFAULT_POLL_INTERVAL);
186 187 188 189 190 191 192 193 194 195 196 197 198

            if (rc == WAIT_OBJECT_0)
                break;
            else if (rc != WAIT_TIMEOUT)
                ERR("Wait returned unexpected value %x\n",rc);
        }
    }
    else
    {
        INT exit_now = FALSE;

        while(1)
        {
199
            KEVENT event;
200

201
            KeInitializeEvent(&event, NotificationEvent, FALSE);
202

203 204
            irp = IoBuildDeviceIoControlRequest(IOCTL_HID_READ_REPORT, ext->u.pdo.parent_fdo,
                    NULL, 0, packet->reportBuffer, report_size, TRUE, &event, &irp_status);
205

206
            if (IoCallDriver(ext->u.pdo.parent_fdo, irp) == STATUS_PENDING)
207
                KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
208

209
            rc = WaitForSingleObject(ext->u.pdo.halt_event, 0);
210 211 212
            if (rc == WAIT_OBJECT_0)
                exit_now = TRUE;

213
            if (!exit_now && irp_status.u.Status == STATUS_SUCCESS)
214
            {
215
                packet->reportBufferLen = irp_status.Information;
216
                if (ext->u.pdo.preparsed_data->reports[0].reportID)
217 218 219
                    packet->reportId = packet->reportBuffer[0];
                else
                    packet->reportId = 0;
220
                RingBuffer_Write(ext->u.pdo.ring_buffer, packet);
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
                HID_Device_processQueue(device);
            }

            if (exit_now)
                break;
        }
    }

    TRACE("Device thread exiting\n");
    return 1;
}

void HID_StartDeviceThread(DEVICE_OBJECT *device)
{
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
236 237
    ext->u.pdo.halt_event = CreateEventA(NULL, TRUE, FALSE, NULL);
    ext->u.pdo.thread = CreateThread(NULL, 0, hid_device_thread, device, 0, NULL);
238 239
}

240
static NTSTATUS handle_IOCTL_HID_GET_COLLECTION_INFORMATION(IRP *irp, BASE_DEVICE_EXTENSION *ext)
241 242 243 244 245 246 247 248 249
{
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
    if (irpsp->Parameters.DeviceIoControl.OutputBufferLength <  sizeof(HID_COLLECTION_INFORMATION))
    {
        irp->IoStatus.u.Status = STATUS_BUFFER_OVERFLOW;
        irp->IoStatus.Information = 0;
    }
    else
    {
250
        memcpy(irp->AssociatedIrp.SystemBuffer, &ext->u.pdo.information, sizeof(HID_COLLECTION_INFORMATION));
251 252 253 254 255 256
        irp->IoStatus.Information = sizeof(HID_COLLECTION_INFORMATION);
        irp->IoStatus.u.Status = STATUS_SUCCESS;
    }
    return STATUS_SUCCESS;
}

257
static NTSTATUS handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR(IRP *irp, BASE_DEVICE_EXTENSION *ext)
258 259
{
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
260
    const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
261

262
    if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < data->dwSize)
263 264 265 266 267 268
    {
        irp->IoStatus.u.Status = STATUS_INVALID_BUFFER_SIZE;
        irp->IoStatus.Information = 0;
    }
    else
    {
269 270
        memcpy(irp->UserBuffer, data, data->dwSize);
        irp->IoStatus.Information = data->dwSize;
271 272 273 274 275
        irp->IoStatus.u.Status = STATUS_SUCCESS;
    }
    return STATUS_SUCCESS;
}

276
static NTSTATUS handle_minidriver_string(BASE_DEVICE_EXTENSION *ext, IRP *irp, SHORT index)
277 278 279 280
{
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
    WCHAR buffer[127];
    NTSTATUS status;
281
    ULONG InputBuffer;
282

283
    InputBuffer = MAKELONG(index, 0);
284 285
    status = call_minidriver(IOCTL_HID_GET_STRING, ext->u.pdo.parent_fdo,
            ULongToPtr(InputBuffer), sizeof(InputBuffer), buffer, sizeof(buffer));
286 287 288

    if (status == STATUS_SUCCESS)
    {
289
        WCHAR *out_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
290 291 292 293 294 295 296 297 298 299
        int length = irpsp->Parameters.DeviceIoControl.OutputBufferLength/sizeof(WCHAR);
        TRACE("got string %s from minidriver\n",debugstr_w(buffer));
        lstrcpynW(out_buffer, buffer, length);
        irp->IoStatus.Information = (lstrlenW(buffer)+1) * sizeof(WCHAR);
    }
    irp->IoStatus.u.Status = status;

    return STATUS_SUCCESS;
}

300
static NTSTATUS HID_get_feature(BASE_DEVICE_EXTENSION *ext, IRP *irp)
301 302 303 304 305
{
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
    HID_XFER_PACKET *packet;
    DWORD len;
    NTSTATUS rc = STATUS_SUCCESS;
306
    BYTE *out_buffer;
307 308 309

    irp->IoStatus.Information = 0;

310
    out_buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
311
    TRACE_(hid_report)("Device %p Buffer length %i Buffer %p\n", ext, irpsp->Parameters.DeviceIoControl.OutputBufferLength, out_buffer);
312 313 314 315 316 317 318 319 320

    len = sizeof(*packet) + irpsp->Parameters.DeviceIoControl.OutputBufferLength;
    packet = HeapAlloc(GetProcessHeap(), 0, len);
    packet->reportBufferLen = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
    packet->reportBuffer = ((BYTE*)packet) + sizeof(*packet);
    packet->reportId = out_buffer[0];

    TRACE_(hid_report)("(id %i, len %i buffer %p)\n", packet->reportId, packet->reportBufferLen, packet->reportBuffer);

321
    rc = call_minidriver(IOCTL_HID_GET_FEATURE, ext->u.pdo.parent_fdo, NULL, 0, packet, sizeof(*packet));
322 323 324

    irp->IoStatus.u.Status = rc;
    if (irp->IoStatus.u.Status == STATUS_SUCCESS)
325 326 327 328
    {
        irp->IoStatus.Information = packet->reportBufferLen;
        memcpy(out_buffer, packet->reportBuffer, packet->reportBufferLen);
    }
329 330 331 332 333
    else
        irp->IoStatus.Information = 0;

    TRACE_(hid_report)("Result 0x%x get %li bytes\n", rc, irp->IoStatus.Information);

334 335
    HeapFree(GetProcessHeap(), 0, packet);

336 337 338
    return rc;
}

339
static NTSTATUS HID_set_to_device(DEVICE_OBJECT *device, IRP *irp)
340
{
341
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
342
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
343
    const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
344
    HID_XFER_PACKET packet;
345
    ULONG max_len;
346 347 348
    NTSTATUS rc;

    TRACE_(hid_report)("Device %p Buffer length %i Buffer %p\n", device, irpsp->Parameters.DeviceIoControl.InputBufferLength, irp->AssociatedIrp.SystemBuffer);
349 350 351 352 353
    packet.reportId = ((BYTE*)irp->AssociatedIrp.SystemBuffer)[0];
    if (packet.reportId == 0)
    {
        packet.reportBuffer = &((BYTE*)irp->AssociatedIrp.SystemBuffer)[1];
        packet.reportBufferLen = irpsp->Parameters.DeviceIoControl.InputBufferLength - 1;
354
        if (irpsp->Parameters.DeviceIoControl.IoControlCode == IOCTL_HID_SET_FEATURE)
355
            max_len = data->caps.FeatureReportByteLength;
356
        else
357
            max_len = data->caps.OutputReportByteLength;
358 359 360 361 362
    }
    else
    {
        packet.reportBuffer = irp->AssociatedIrp.SystemBuffer;
        packet.reportBufferLen = irpsp->Parameters.DeviceIoControl.InputBufferLength;
363
        if (irpsp->Parameters.DeviceIoControl.IoControlCode == IOCTL_HID_SET_FEATURE)
364
            max_len = data->reports[data->reportIdx[HidP_Feature][packet.reportId]].bitSize;
365
        else
366 367
            max_len = data->reports[data->reportIdx[HidP_Output][packet.reportId]].bitSize;
        max_len = (max_len + 7) / 8;
368
    }
369 370 371
    if (packet.reportBufferLen > max_len)
        packet.reportBufferLen = max_len;

372 373
    TRACE_(hid_report)("(id %i, len %i buffer %p)\n", packet.reportId, packet.reportBufferLen, packet.reportBuffer);

374
    rc = call_minidriver(irpsp->Parameters.DeviceIoControl.IoControlCode,
375
            ext->u.pdo.parent_fdo, NULL, 0, &packet, sizeof(packet));
376 377 378 379 380 381 382 383 384 385 386 387

    irp->IoStatus.u.Status = rc;
    if (irp->IoStatus.u.Status == STATUS_SUCCESS)
        irp->IoStatus.Information = irpsp->Parameters.DeviceIoControl.InputBufferLength;
    else
        irp->IoStatus.Information = 0;

    TRACE_(hid_report)("Result 0x%x set %li bytes\n", rc, irp->IoStatus.Information);

    return rc;
}

388
NTSTATUS WINAPI pdo_ioctl(DEVICE_OBJECT *device, IRP *irp)
389 390 391
{
    NTSTATUS rc = STATUS_SUCCESS;
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
392
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

    irp->IoStatus.Information = 0;

    TRACE("device %p ioctl(%x)\n", device, irpsp->Parameters.DeviceIoControl.IoControlCode);

    switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
    {
        case IOCTL_HID_GET_POLL_FREQUENCY_MSEC:
            TRACE("IOCTL_HID_GET_POLL_FREQUENCY_MSEC\n");
            if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
            {
                irp->IoStatus.u.Status = STATUS_BUFFER_OVERFLOW;
                irp->IoStatus.Information = 0;
                break;
            }
408
            *(ULONG *)irp->AssociatedIrp.SystemBuffer = ext->u.pdo.poll_interval;
409 410 411 412 413 414 415 416 417 418 419 420 421
            irp->IoStatus.Information = sizeof(ULONG);
            irp->IoStatus.u.Status = STATUS_SUCCESS;
            break;
        case IOCTL_HID_SET_POLL_FREQUENCY_MSEC:
        {
            ULONG poll_interval;
            TRACE("IOCTL_HID_SET_POLL_FREQUENCY_MSEC\n");
            if (irpsp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG))
            {
                irp->IoStatus.u.Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }
            poll_interval = *(ULONG *)irp->AssociatedIrp.SystemBuffer;
422
            if (poll_interval <= MAX_POLL_INTERVAL_MSEC)
423
            {
424
                ext->u.pdo.poll_interval = poll_interval;
425 426 427 428 429 430 431 432
                irp->IoStatus.u.Status = STATUS_SUCCESS;
            }
            else
                irp->IoStatus.u.Status = STATUS_INVALID_PARAMETER;
            break;
        }
        case IOCTL_HID_GET_PRODUCT_STRING:
        {
433
            rc = handle_minidriver_string(ext, irp, HID_STRING_ID_IPRODUCT);
434 435
            break;
        }
436 437
        case IOCTL_HID_GET_SERIALNUMBER_STRING:
        {
438
            rc = handle_minidriver_string(ext, irp, HID_STRING_ID_ISERIALNUMBER);
439 440
            break;
        }
441 442
        case IOCTL_HID_GET_MANUFACTURER_STRING:
        {
443
            rc = handle_minidriver_string(ext, irp, HID_STRING_ID_IMANUFACTURER);
444 445 446 447
            break;
        }
        case IOCTL_HID_GET_COLLECTION_INFORMATION:
        {
448
            rc = handle_IOCTL_HID_GET_COLLECTION_INFORMATION(irp, ext);
449 450 451 452
            break;
        }
        case IOCTL_HID_GET_COLLECTION_DESCRIPTOR:
        {
453
            rc = handle_IOCTL_HID_GET_COLLECTION_DESCRIPTOR(irp, ext);
454 455 456 457
            break;
        }
        case IOCTL_HID_GET_INPUT_REPORT:
        {
458 459
            HID_XFER_PACKET *packet;
            UINT packet_size = sizeof(*packet) + irpsp->Parameters.DeviceIoControl.OutputBufferLength;
460
            BYTE *buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
461 462 463
            ULONG out_length;

            packet = HeapAlloc(GetProcessHeap(), 0, packet_size);
464

465
            if (ext->u.pdo.preparsed_data->reports[0].reportID)
466
                packet->reportId = buffer[0];
467
            else
468 469 470
                packet->reportId = 0;
            packet->reportBuffer = (BYTE *)packet + sizeof(*packet);
            packet->reportBufferLen = irpsp->Parameters.DeviceIoControl.OutputBufferLength - 1;
471

472
            rc = call_minidriver(IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, packet, sizeof(*packet));
473 474 475 476 477 478 479 480 481
            if (rc == STATUS_SUCCESS)
            {
                rc = copy_packet_into_buffer(packet, buffer, irpsp->Parameters.DeviceIoControl.OutputBufferLength, &out_length);
                irp->IoStatus.Information = out_length;
            }
            else
                irp->IoStatus.Information = 0;
            irp->IoStatus.u.Status = rc;
            HeapFree(GetProcessHeap(), 0, packet);
482 483
            break;
        }
484 485 486 487 488 489 490 491 492 493
        case IOCTL_SET_NUM_DEVICE_INPUT_BUFFERS:
        {
            irp->IoStatus.Information = 0;

            if (irpsp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG))
            {
                irp->IoStatus.u.Status = rc = STATUS_BUFFER_OVERFLOW;
            }
            else
            {
494
                rc = RingBuffer_SetSize(ext->u.pdo.ring_buffer, *(ULONG *)irp->AssociatedIrp.SystemBuffer);
495 496 497 498 499 500 501 502 503 504 505 506
                irp->IoStatus.u.Status = rc;
            }
            break;
        }
        case IOCTL_GET_NUM_DEVICE_INPUT_BUFFERS:
        {
            if (irpsp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
            {
                irp->IoStatus.u.Status = rc = STATUS_BUFFER_TOO_SMALL;
            }
            else
            {
507
                *(ULONG *)irp->AssociatedIrp.SystemBuffer = RingBuffer_GetSize(ext->u.pdo.ring_buffer);
508 509 510 511
                rc = irp->IoStatus.u.Status = STATUS_SUCCESS;
            }
            break;
        }
512
        case IOCTL_HID_GET_FEATURE:
513
            rc = HID_get_feature(ext, irp);
514
            break;
515
        case IOCTL_HID_SET_FEATURE:
516 517
        case IOCTL_HID_SET_OUTPUT_REPORT:
            rc = HID_set_to_device(device, irp);
518
            break;
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
        default:
        {
            ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
            FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
                  code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3);
            irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
            rc = STATUS_UNSUCCESSFUL;
            break;
        }
    }

    if (rc != STATUS_PENDING)
        IoCompleteRequest( irp, IO_NO_INCREMENT );

    return rc;
}
535

536
NTSTATUS WINAPI pdo_read(DEVICE_OBJECT *device, IRP *irp)
537 538 539
{
    HID_XFER_PACKET *packet;
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
540
    UINT buffer_size = RingBuffer_GetBufferSize(ext->u.pdo.ring_buffer);
541
    NTSTATUS rc = STATUS_SUCCESS;
542
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
543 544 545 546 547 548
    int ptr = -1;

    packet = HeapAlloc(GetProcessHeap(), 0, buffer_size);
    ptr = PtrToUlong( irp->Tail.Overlay.OriginalFileObject->FsContext );

    irp->IoStatus.Information = 0;
549
    RingBuffer_ReadNew(ext->u.pdo.ring_buffer, ptr, packet, &buffer_size);
550 551 552 553

    if (buffer_size)
    {
        IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
554 555
        NTSTATUS rc;
        ULONG out_length;
556
        packet->reportBuffer = (BYTE *)packet + sizeof(*packet);
557
        TRACE_(hid_report)("Got Packet %p %i\n", packet->reportBuffer, packet->reportBufferLen);
558 559 560 561 562

        rc = copy_packet_into_buffer(packet, irp->AssociatedIrp.SystemBuffer, irpsp->Parameters.Read.Length, &out_length);
        irp->IoStatus.Information = out_length;
        irp->IoStatus.u.Status = rc;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
563 564 565
    }
    else
    {
566
        if (ext->u.pdo.poll_interval)
567
        {
568
            KIRQL old_irql;
569
            TRACE_(hid_report)("Queue irp\n");
570

571
            KeAcquireSpinLock(&ext->u.pdo.irp_queue_lock, &old_irql);
572

573 574 575 576 577
            IoSetCancelRoutine(irp, read_cancel_routine);
            if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
            {
                /* IRP was canceled before we set cancel routine */
                InitializeListHead(&irp->Tail.Overlay.s.ListEntry);
578
                KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
579 580 581
                return STATUS_CANCELLED;
            }

582
            InsertTailList(&ext->u.pdo.irp_queue, &irp->Tail.Overlay.s.ListEntry);
583
            IoMarkIrpPending(irp);
584

585
            KeReleaseSpinLock(&ext->u.pdo.irp_queue_lock, old_irql);
586
            rc = STATUS_PENDING;
587 588 589 590 591 592 593 594
        }
        else
        {
            HID_XFER_PACKET packet;
            TRACE("No packet, but opportunistic reads enabled\n");
            packet.reportId = ((BYTE*)irp->AssociatedIrp.SystemBuffer)[0];
            packet.reportBuffer = &((BYTE*)irp->AssociatedIrp.SystemBuffer)[1];
            packet.reportBufferLen = irpsp->Parameters.Read.Length - 1;
595
            rc = call_minidriver(IOCTL_HID_GET_INPUT_REPORT, ext->u.pdo.parent_fdo, NULL, 0, &packet, sizeof(packet));
596 597 598 599 600 601 602 603 604

            if (rc == STATUS_SUCCESS)
            {
                ((BYTE*)irp->AssociatedIrp.SystemBuffer)[0] = packet.reportId;
                irp->IoStatus.Information = packet.reportBufferLen + 1;
                irp->IoStatus.u.Status = rc;
            }
            IoCompleteRequest(irp, IO_NO_INCREMENT);
        }
605 606 607 608 609 610
    }
    HeapFree(GetProcessHeap(), 0, packet);

    return rc;
}

611
NTSTATUS WINAPI pdo_write(DEVICE_OBJECT *device, IRP *irp)
612 613
{
    IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
614
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
615
    const WINE_HIDP_PREPARSED_DATA *data = ext->u.pdo.preparsed_data;
616
    HID_XFER_PACKET packet;
617
    ULONG max_len;
618
    NTSTATUS rc;
619 620 621

    irp->IoStatus.Information = 0;

622
    TRACE_(hid_report)("Device %p Buffer length %i Buffer %p\n", device, irpsp->Parameters.Write.Length, irp->AssociatedIrp.SystemBuffer);
623 624 625 626 627
    packet.reportId = ((BYTE*)irp->AssociatedIrp.SystemBuffer)[0];
    if (packet.reportId == 0)
    {
        packet.reportBuffer = &((BYTE*)irp->AssociatedIrp.SystemBuffer)[1];
        packet.reportBufferLen = irpsp->Parameters.Write.Length - 1;
628
        max_len = data->caps.OutputReportByteLength;
629 630 631 632 633
    }
    else
    {
        packet.reportBuffer = irp->AssociatedIrp.SystemBuffer;
        packet.reportBufferLen = irpsp->Parameters.Write.Length;
634
        max_len = (data->reports[data->reportIdx[HidP_Output][packet.reportId]].bitSize + 7) / 8;
635
    }
636 637 638
    if (packet.reportBufferLen > max_len)
        packet.reportBufferLen = max_len;

639
    TRACE_(hid_report)("(id %i, len %i buffer %p)\n", packet.reportId, packet.reportBufferLen, packet.reportBuffer);
640

641
    rc = call_minidriver(IOCTL_HID_WRITE_REPORT, ext->u.pdo.parent_fdo, NULL, 0, &packet, sizeof(packet));
642 643 644 645 646 647 648 649

    irp->IoStatus.u.Status = rc;
    if (irp->IoStatus.u.Status == STATUS_SUCCESS)
        irp->IoStatus.Information = irpsp->Parameters.Write.Length;
    else
        irp->IoStatus.Information = 0;

    TRACE_(hid_report)("Result 0x%x wrote %li bytes\n", rc, irp->IoStatus.Information);
650 651

    IoCompleteRequest( irp, IO_NO_INCREMENT );
652
    return rc;
653 654
}

655
NTSTATUS WINAPI pdo_create(DEVICE_OBJECT *device, IRP *irp)
656 657 658 659
{
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;

    TRACE("Open handle on device %p\n", device);
660
    irp->Tail.Overlay.OriginalFileObject->FsContext = UlongToPtr(RingBuffer_AddPointer(ext->u.pdo.ring_buffer));
661 662 663 664 665
    irp->IoStatus.u.Status = STATUS_SUCCESS;
    IoCompleteRequest( irp, IO_NO_INCREMENT );
    return STATUS_SUCCESS;
}

666
NTSTATUS WINAPI pdo_close(DEVICE_OBJECT *device, IRP *irp)
667 668 669 670
{
    BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
    int ptr = PtrToUlong(irp->Tail.Overlay.OriginalFileObject->FsContext);
    TRACE("Close handle on device %p\n", device);
671
    RingBuffer_RemovePointer(ext->u.pdo.ring_buffer, ptr);
672 673 674 675
    irp->IoStatus.u.Status = STATUS_SUCCESS;
    IoCompleteRequest( irp, IO_NO_INCREMENT );
    return STATUS_SUCCESS;
}