// Pass down Idle notification request to lower driver
//
NTSTATUS HidFx2SendIdleNotification(_In_ WDFREQUEST hRequest)
{
    NTSTATUS                   status = STATUS_SUCCESS;
    WDF_REQUEST_SEND_OPTIONS   options;
    WDFIOTARGET                hNextLowerDriver;
    WDFDEVICE                  hDevice;
    PIO_STACK_LOCATION         pCurrentIrpStack = NULL;
    IO_STACK_LOCATION          nextIrpStack;

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Entry\n");

    hDevice = WdfIoQueueGetDevice(WdfRequestGetIoQueue(hRequest));
    pCurrentIrpStack = IoGetCurrentIrpStackLocation(WdfRequestWdmGetIrp(hRequest));

    // Convert the request to corresponding USB Idle notification request
    if (pCurrentIrpStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO))
    {
        ASSERT(sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) == sizeof(USB_IDLE_CALLBACK_INFO));
        #pragma warning(suppress :4127)  // conditional expression is constant warning
        if (sizeof(HID_SUBMIT_IDLE_NOTIFICATION_CALLBACK_INFO) == sizeof(USB_IDLE_CALLBACK_INFO))
        {
            // prepare next stack location
            RtlZeroMemory(&nextIrpStack, sizeof(IO_STACK_LOCATION));
            nextIrpStack.MajorFunction = pCurrentIrpStack->MajorFunction;
            nextIrpStack.Parameters.DeviceIoControl.InputBufferLength = pCurrentIrpStack->Parameters.DeviceIoControl.InputBufferLength;
            nextIrpStack.Parameters.DeviceIoControl.Type3InputBuffer = pCurrentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
            nextIrpStack.Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
            nextIrpStack.DeviceObject = WdfIoTargetWdmGetTargetDeviceObject(WdfDeviceGetIoTarget(hDevice));

            // Format the I/O request for the driver's local I/O target by using the contents of the specified WDM I/O stack location structure.
            WdfRequestWdmFormatUsingStackLocation(hRequest, &nextIrpStack);

            // Send the request down using Fire and forget option.
            WDF_REQUEST_SEND_OPTIONS_INIT(&options, WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET);
            hNextLowerDriver = WdfDeviceGetIoTarget(hDevice);
            if (WdfRequestSend(hRequest, hNextLowerDriver, &options) == FALSE)
            {
                status = STATUS_UNSUCCESSFUL;
            }
        }
        else // Incorrect DeviceIoControl.InputBufferLength
        {
            status = STATUS_INFO_LENGTH_MISMATCH;
            TraceErr(DBG_IOCTL, "(%!FUNC!) Incorrect DeviceIoControl.InputBufferLength, %!STATUS!\n", status);
            return status;
        }
    }
    else // DeviceIoControl.InputBufferLength too small
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) DeviceIoControl.InputBufferLength too small, %!STATUS!\n", status);
        return status;
    }

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Exit = %!STATUS!\n", status);
    return status;
}
static VOID
XenVbd_StopRing(PXENVBD_DEVICE_DATA xvdd, BOOLEAN suspend) {
  PXENVBD_FILTER_DATA xvfd = (PXENVBD_FILTER_DATA)xvdd->xvfd;
  NTSTATUS status;
  WDFREQUEST request;
  WDF_REQUEST_SEND_OPTIONS send_options;
  IO_STACK_LOCATION stack;
  SCSI_REQUEST_BLOCK srb;
  SRB_IO_CONTROL sic;

  FUNCTION_ENTER();
  
  /* send a 'stop' down if we are suspending */
  if (suspend) {
    status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
    FUNCTION_MSG("WdfRequestCreate = %08x\n", status);

    RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
    stack.MajorFunction = IRP_MJ_SCSI;
    stack.MinorFunction = IRP_MN_SCSI_CLASS;
    stack.Parameters.Scsi.Srb = &srb;

    RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
    srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
    srb.Length = SCSI_REQUEST_BLOCK_SIZE;
    srb.PathId = 0;
    srb.TargetId = 0;
    srb.Lun = 0;
    srb.OriginalRequest = WdfRequestWdmGetIrp(request);
    srb.Function = SRB_FUNCTION_IO_CONTROL;
    srb.DataBuffer = &sic;
    
    RtlZeroMemory(&sic, sizeof(SRB_IO_CONTROL));
    sic.HeaderLength = sizeof(SRB_IO_CONTROL);
    memcpy(sic.Signature, XENVBD_CONTROL_SIG, 8);
    sic.Timeout = 60;
    sic.ControlCode = XENVBD_CONTROL_STOP;
    
    WdfRequestWdmFormatUsingStackLocation(request, &stack);
    
    WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
    if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
      FUNCTION_MSG("Request was _NOT_ sent\n");
    }
    #if DBG
    status = WdfRequestGetStatus(request);
    FUNCTION_MSG("Request Status = %08x\n", status);
    FUNCTION_MSG("SRB Status = %08x\n", srb.SrbStatus);
    #endif

    WdfObjectDelete(request);
  }
  
  status = XnWriteInt32(xvdd->handle, XN_BASE_FRONTEND, "state", XenbusStateClosing);

  FUNCTION_EXIT();
}
static VOID
XenVbd_SendEvent(WDFDEVICE device) {
  PXENVBD_FILTER_DATA xvfd = GetXvfd(device);
  NTSTATUS status;
  WDFREQUEST request;
  WDF_REQUEST_SEND_OPTIONS send_options;
  IO_STACK_LOCATION stack;
  PUCHAR buf;
  PSCSI_REQUEST_BLOCK srb;
  PSRB_IO_CONTROL sic;

  status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, xvfd->wdf_target, &request);
  if (status != STATUS_SUCCESS) {
    FUNCTION_MSG("WdfRequestCreate failed %08x\n", status);
    /* this is bad - event will be dropped */
    return;
  }

  buf = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL) + sizeof(LARGE_INTEGER), XENVBD_POOL_TAG);
  RtlZeroMemory(buf, sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL));
  srb = (PSCSI_REQUEST_BLOCK)(buf);
  sic = (PSRB_IO_CONTROL)(buf + sizeof(SCSI_REQUEST_BLOCK));
  
  srb->Length = sizeof(SCSI_REQUEST_BLOCK);
  srb->SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
  srb->PathId = 0;
  srb->TargetId = 0;
  srb->Lun = 0;
  srb->OriginalRequest = WdfRequestWdmGetIrp(request);
  srb->Function = SRB_FUNCTION_IO_CONTROL;
  srb->DataBuffer = sic;
  srb->DataTransferLength = sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL);
  srb->TimeOutValue = (ULONG)-1;
  
  sic->HeaderLength = sizeof(SRB_IO_CONTROL);
  memcpy(sic->Signature, XENVBD_CONTROL_SIG, 8);
  sic->Timeout = (ULONG)-1;
  sic->ControlCode = XENVBD_CONTROL_EVENT;
  
  KeQuerySystemTime((PLARGE_INTEGER)((PUCHAR)buf + sizeof(SCSI_REQUEST_BLOCK) + sizeof(SRB_IO_CONTROL)));

  RtlZeroMemory(&stack, sizeof(IO_STACK_LOCATION));
  stack.MajorFunction = IRP_MJ_SCSI;
  stack.MinorFunction = IRP_MN_SCSI_CLASS;
  stack.Parameters.Scsi.Srb = srb;

  WdfRequestWdmFormatUsingStackLocation(request, &stack);
  WdfRequestSetCompletionRoutine(request, XenVbd_SendEventComplete, buf);
  
  WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, 0); //WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
  if (!WdfRequestSend(request, xvfd->wdf_target, &send_options)) {
    FUNCTION_MSG("Error sending request\n");
  }
}
NTSTATUS HidFx2SetOutput(_In_ WDFREQUEST hRequest)
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PHID_XFER_PACKET            pTransferPacket = NULL;
    WDF_REQUEST_PARAMETERS      params;
    PHIDFX2_IO_REPORT           pOutputReport = NULL;
    WDFDEVICE                   hDevice;

    PAGED_CODE();

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Enter\n");

    hDevice = WdfIoQueueGetDevice(WdfRequestGetIoQueue(hRequest));

    WDF_REQUEST_PARAMETERS_INIT(&params);
    WdfRequestGetParameters(hRequest, &params);

    if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Userbuffer is small %!STATUS!\n", status);
        return status;
    }

    pTransferPacket = (PHID_XFER_PACKET) WdfRequestWdmGetIrp(hRequest)->UserBuffer;
    if (pTransferPacket == NULL)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Irp->UserBuffer is NULL %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportBufferLen == 0)
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) HID_XFER_PACKET->reportBufferLen is 0, %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportBufferLen < sizeof(UCHAR))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) HID_XFER_PACKET->reportBufferLen is too small, %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportId != GENERIC_DESKTOP_REPORT_ID)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Incorrect report ID, %!STATUS!\n", status);
        return status;
    }

    pOutputReport = (PHIDFX2_IO_REPORT)pTransferPacket->reportBuffer;

    if (pOutputReport->bData == 0)
    {
        status = SendVendorCommand(hDevice, HIDFX2_SET_BARGRAPH_DISPLAY, BARGRAPH_LED_ALL_OFF);
    }
    else
    {
        status = SendVendorCommand(hDevice, HIDFX2_SET_BARGRAPH_DISPLAY, BARGRAPH_LED_ALL_ON);
    }

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Exit status %!STATUS!\n", status);
    return status;
}
NTSTATUS HidFx2GetInput(_In_ WDFREQUEST hRequest)
{
    NTSTATUS                    status = STATUS_SUCCESS;
    WDF_REQUEST_PARAMETERS      params;
    WDFDEVICE                   hDevice;
    PHID_XFER_PACKET            pTransferPacket = NULL;
    PHIDFX2_IO_REPORT           pOutputReport = NULL;
    unsigned char               bSwitchState = 0;

    PAGED_CODE();

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Enter\n");

    hDevice = WdfIoQueueGetDevice(WdfRequestGetIoQueue(hRequest));

    WDF_REQUEST_PARAMETERS_INIT(&params);
    WdfRequestGetParameters(hRequest, &params);


    if (params.Parameters.DeviceIoControl.OutputBufferLength < sizeof(HID_XFER_PACKET))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Userbuffer is small %!STATUS!\n", status);
        return status;
    }

    pTransferPacket = (PHID_XFER_PACKET) WdfRequestWdmGetIrp(hRequest)->UserBuffer;
    if (pTransferPacket == NULL)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Irp->UserBuffer is NULL %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportBufferLen == 0)
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) HID_XFER_PACKET->reportBufferLen is 0, %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportBufferLen < sizeof(UCHAR))
    {
        status = STATUS_BUFFER_TOO_SMALL;
        TraceErr(DBG_IOCTL, "(%!FUNC!) HID_XFER_PACKET->reportBufferLen is too small, %!STATUS!\n", status);
        return status;
    }

    if (pTransferPacket->reportId != GENERIC_DESKTOP_REPORT_ID)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        TraceErr(DBG_IOCTL, "(%!FUNC!) Incorrect report ID, %!STATUS!\n", status);
        return status;
    }

    pOutputReport = (PHIDFX2_IO_REPORT)pTransferPacket->reportBuffer;

    // Get the switch state directly from the hardware
    status = HidFx2GetSwitchState(hDevice, &bSwitchState);
    TraceVerbose(DBG_IOCTL, "(%!FUNC!) switch state 0x%x\n", bSwitchState);

    //Mask off everything except the actual switch bit
    bSwitchState &= RADIO_SWITCH_BUTTONS_BIT_MASK;
    TraceVerbose(DBG_IOCTL, "(%!FUNC!) maskedswitch state 0x%x\n", bSwitchState);

    pOutputReport->bReportId = GENERIC_DESKTOP_REPORT_ID;
    pOutputReport->bData = bSwitchState;

    TraceVerbose(DBG_IOCTL, "(%!FUNC!) Exit status %!STATUS!\n", status);
    return status;
}
Beispiel #6
0
/**
 * @brief DPC callback.
 * Calls the Xen interface api to process the ringbuffer until the ringbuffer is empty,
 * then completes all requests provided by the Xen api.
 *
 * @todo consider completion within the XenDpc() loop. However this would release the device
 * lock.
 *
 * @param[in] Dpc The WDFDPC handle.
 *  
 */
VOID
FdoEvtDeviceDpcFunc(
    IN VOID *Context)
{
    // --XT-- FDO context passed directly now.
    PUSB_FDO_CONTEXT fdoContext = (PUSB_FDO_CONTEXT)Context;
    //
    // this stuff needs to be done at DPC level in order to complete irps.
    //
    ULONG passes = 0;
    AcquireFdoLock(fdoContext);

    if (fdoContext->InDpc)
    {
        fdoContext->DpcOverLapCount++;
        ReleaseFdoLock(fdoContext);
        return;
    }
    fdoContext->InDpc = TRUE;

    BOOLEAN moreWork;
    do
    {
        moreWork = XenDpc(fdoContext, fdoContext->RequestCollection);
        passes++;
        if (fdoContext->DpcOverLapCount)
        {
            fdoContext->totalDpcOverLapCount += fdoContext->DpcOverLapCount;
            fdoContext->DpcOverLapCount = 0;
            moreWork = TRUE;
        }
        if (moreWork & (passes >= KeQueryActiveProcessorCount(NULL)))
        {
            //
            // reschedule the dpc to prevent starvation.
            //
            // --XT-- WdfDpcEnqueue(fdoContext->WdfDpc);
            //
            // --XT-- Schedule through the Xen interface now.
            //
            XenScheduleDPC(fdoContext->Xen);
            TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DPC,
                __FUNCTION__": enqueue dpc at %d passes\n",
                passes);
            fdoContext->totalDpcReQueueCount++;
            break;
        }

    } while (moreWork);
 
    fdoContext->InDpc = FALSE; // allow another dpc instance to run and add to the collection.
    if (passes > fdoContext->maxDpcPasses)
    {
        fdoContext->maxDpcPasses = passes;
    }
    //
    // complete all queued Irps. Note that this section is re-entrant.
    // Note also that all of these requests have been "uncanceled" and ahve
    // been marked as completed in their request contexts and that the
    // additional reference on the request object has been removed.
    // The collection can be safely completed with no race conditions.
    //
    ULONG responseCount = 0;
    WDFREQUEST Request;
    while ((Request = (WDFREQUEST) WdfCollectionGetFirstItem(fdoContext->RequestCollection)) != NULL)
    {
        WdfCollectionRemoveItem(fdoContext->RequestCollection, 0);

        TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DPC,
            __FUNCTION__": complete Request %p Status %x\n",
            Request,
            WdfRequestWdmGetIrp(Request)->IoStatus.Status);
        
        ReleaseFdoLock(fdoContext);

        WdfRequestCompleteWithPriorityBoost(Request,
            WdfRequestWdmGetIrp(Request)->IoStatus.Status,
            IO_SOUND_INCREMENT);
        
        AcquireFdoLock(fdoContext);

        responseCount++;
    }

    if (responseCount > fdoContext->maxRequestsProcessed)
    {
        fdoContext->maxRequestsProcessed = responseCount;
    }
    //
    // fire up any queued requests
    //    
    DrainRequestQueue(fdoContext, FALSE);

    ReleaseFdoLock(fdoContext);

    // --XT-- this trace was way too noisy, made it verbose.
    TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DPC,
        __FUNCTION__": exit responses processed %d passes %d\n",
        responseCount,
        passes);
}
Beispiel #7
0
NTSTATUS
PowerContextReuseRequest(
    _In_ PCDROM_DEVICE_EXTENSION DeviceExtension
    )
/*++

Routine Description:

    reset fields for the request.

Arguments:

    DeviceExtension - device context

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                 status = STATUS_SUCCESS;
    WDF_REQUEST_REUSE_PARAMS reuseParams;
    PIRP                     irp = NULL;

    RtlZeroMemory(&(DeviceExtension->PowerContext.SenseData), sizeof(DeviceExtension->PowerContext.SenseData));
    RtlZeroMemory(&(DeviceExtension->PowerContext.Srb), sizeof(DeviceExtension->PowerContext.Srb));

    irp = WdfRequestWdmGetIrp(DeviceExtension->PowerContext.PowerRequest);

    // Re-use the previously created PowerRequest object and format it
    WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
    status = WdfRequestReuse(DeviceExtension->PowerContext.PowerRequest, &reuseParams);
    if (NT_SUCCESS(status))
    {
        // This request was preformated during initialization so this call should never fail.
        status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 
                                                                DeviceExtension->PowerContext.PowerRequest,
                                                                IOCTL_SCSI_EXECUTE_IN,
                                                                NULL, NULL,
                                                                NULL, NULL,
                                                                NULL, NULL);

        if (!NT_SUCCESS(status))
        {
            TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,  
                       "PowerContextReuseRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
                       status));
        }
    }

    // Do some basic initialization of the PowerRequest, the rest will be done by the caller
    // of this function
    if (NT_SUCCESS(status))
    {
        PIO_STACK_LOCATION  nextStack = NULL;

        nextStack = IoGetNextIrpStackLocation(irp);

        nextStack->MajorFunction = IRP_MJ_SCSI;
        nextStack->Parameters.Scsi.Srb = &(DeviceExtension->PowerContext.Srb);

        DeviceExtension->PowerContext.Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
        DeviceExtension->PowerContext.Srb.OriginalRequest = irp;

        DeviceExtension->PowerContext.Srb.SenseInfoBuffer = &(DeviceExtension->PowerContext.SenseData);
        DeviceExtension->PowerContext.Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
    }

    return status;
}
Beispiel #8
0
VOID
EvtIoDeviceControl(
    WDFQUEUE   Queue,
    WDFREQUEST Request,
    size_t     OutputBufferLength,
    size_t     InputBufferLength,
    ULONG      IoControlCode)
{
    NTSTATUS        status;
    BOOLEAN         completeRequest = TRUE;
    WDFDEVICE       device = WdfIoQueueGetDevice(Queue);
    PINPUT_DEVICE   pContext = GetDeviceContext(device);
    ULONG           uReportSize;
    HID_XFER_PACKET Packet;
    WDF_REQUEST_PARAMETERS params;
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(InputBufferLength);

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS,
                "--> %s, code = %d\n", __FUNCTION__, IoControlCode);

    switch (IoControlCode)
    {
    case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
        //
        // Return the device's HID descriptor.
        //
        ASSERT(pContext->HidDescriptor.bLength != 0);
        status = RequestCopyFromBuffer(Request,
            &pContext->HidDescriptor,
            pContext->HidDescriptor.bLength);
        break;

    case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
        //
        // Return the device's attributes in a HID_DEVICE_ATTRIBUTES structure.
        //
        status = RequestCopyFromBuffer(Request,
            &pContext->HidDeviceAttributes,
            sizeof(HID_DEVICE_ATTRIBUTES));
        break;

    case IOCTL_HID_GET_REPORT_DESCRIPTOR:
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
        //
        // Return the report descriptor for the HID device.
        //
        status = RequestCopyFromBuffer(Request,
            pContext->HidReportDescriptor,
            pContext->HidDescriptor.DescriptorList[0].wReportLength);
        break;

    case IOCTL_HID_READ_REPORT:
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_READ_REPORT\n");
        //
        // Queue up a report request. We'll complete it when we actually
        // receive data from the device.
        //

        status = WdfRequestForwardToIoQueue(
            Request,
            pContext->HidQueue);
        if (NT_SUCCESS(status))
        {
            completeRequest = FALSE;
        }
        else
        {
            TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTLS,
                        "WdfRequestForwardToIoQueue failed with 0x%x\n", status);
        }
        break;

    case IOCTL_HID_WRITE_REPORT:
        TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "IOCTL_HID_WRITE_REPORT\n");
        //
        // Write a report to the device, commonly used for controlling keyboard
        // LEDs. We'll complete the request after the host processes all virtio
        // buffers we add to the status queue.
        //

        WDF_REQUEST_PARAMETERS_INIT(&params);
        WdfRequestGetParameters(Request, &params);

        if (params.Parameters.DeviceIoControl.InputBufferLength < sizeof(HID_XFER_PACKET))
        {
            status = STATUS_BUFFER_TOO_SMALL;
        }
        else
        {
            RtlCopyMemory(&Packet, WdfRequestWdmGetIrp(Request)->UserBuffer,
                          sizeof(HID_XFER_PACKET));
            WdfRequestSetInformation(Request, Packet.reportBufferLen);

            status = ProcessOutputReport(pContext, Request, &Packet);
            if (NT_SUCCESS(status))
            {
                completeRequest = FALSE;
            }
        }
        break;

    default:
        TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTLS,
                    "Unrecognized IOCTL %d\n", IoControlCode);
        status = STATUS_NOT_IMPLEMENTED;
        break;
    }

    if (completeRequest)
    {
        WdfRequestComplete(Request, status);
    }

    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "<-- %s\n", __FUNCTION__);
}