Пример #1
0
static NTSTATUS UsbChief_QueuePassiveLevelCallback(IN WDFDEVICE    Device,
				   IN WDFUSBPIPE   Pipe)
{
	NTSTATUS                       status = STATUS_SUCCESS;
	PWORKITEM_CONTEXT               context;
	WDF_OBJECT_ATTRIBUTES           attributes;
	WDF_WORKITEM_CONFIG             workitemConfig;
	WDFWORKITEM                     hWorkItem;

	WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, WORKITEM_CONTEXT);
	attributes.ParentObject = Device;

	WDF_WORKITEM_CONFIG_INIT(&workitemConfig, UsbChief_ReadWriteWorkItem);

	status = WdfWorkItemCreate( &workitemConfig,
				    &attributes,
				    &hWorkItem);

	if (!NT_SUCCESS(status))
		return status;

	context = GetWorkItemContext(hWorkItem);

	context->Device = Device;
	context->Pipe = Pipe;

	WdfWorkItemEnqueue(hWorkItem);
	return STATUS_SUCCESS;
}
Пример #2
0
NTSTATUS
QueuePassiveLevelCallback(
  _In_ WDFDEVICE  Device,
  _In_ WDFUSBPIPE Pipe
)
/*++

Routine Description:

    This routine is used to queue workitems so that the callback
    functions can be executed at PASSIVE_LEVEL in the context of
    a system thread.

Arguments:

Return Value:

--*/
{
  NTSTATUS              status = STATUS_SUCCESS;
  PWORKITEM_CONTEXT     context;
  WDF_OBJECT_ATTRIBUTES attributes;
  WDF_WORKITEM_CONFIG   workitemConfig;
  WDFWORKITEM           hWorkItem;

  WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
  WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, WORKITEM_CONTEXT);
  attributes.ParentObject = Device;

  WDF_WORKITEM_CONFIG_INIT(&workitemConfig, Rio500_EvtReadWriteWorkItem);

  status = WdfWorkItemCreate(
    &workitemConfig,
    &attributes,
    &hWorkItem
  );

  if (!NT_SUCCESS(status)) {
    return status;
  }

  context = GetWorkItemContext(hWorkItem);

  context->Device = Device;
  context->Pipe = Pipe;

  //
  // Execute this work item.
  //
  WdfWorkItemEnqueue(hWorkItem);

  return STATUS_SUCCESS;
}
NTSTATUS StatInitializeWorkItem(
    IN WDFDEVICE  Device
    )
{
    WDF_OBJECT_ATTRIBUTES   attributes;
    WDF_WORKITEM_CONFIG     workitemConfig;
    PDEVICE_CONTEXT devCtx = GetDeviceContext(Device);

    RtlZeroMemory(Counters, sizeof(Counters));

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = Device;
    WDF_WORKITEM_CONFIG_INIT(&workitemConfig, StatWorkItemWorker);
    return WdfWorkItemCreate(&workitemConfig, &attributes, &devCtx->StatWorkItem);
}
Пример #4
0
void CyapaBootTimer(_In_ WDFTIMER hTimer) {
	WDFDEVICE Device = (WDFDEVICE)WdfTimerGetParentObject(hTimer);
	PDEVICE_CONTEXT pDevice = GetDeviceContext(Device);

	WDF_OBJECT_ATTRIBUTES attributes;
	WDF_WORKITEM_CONFIG workitemConfig;
	WDFWORKITEM hWorkItem;

	WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
	attributes.ParentObject = Device;
	WDF_WORKITEM_CONFIG_INIT(&workitemConfig, CyapaBootWorkItem);

	WdfWorkItemCreate(&workitemConfig,
		&attributes,
		&hWorkItem);

	WdfWorkItemEnqueue(hWorkItem);
	WdfTimerStop(hTimer, FALSE);
}
VOID
VIOSerialPortPnpNotify (
    IN WDFDEVICE WdfDevice,
    IN PVIOSERIAL_PORT port,
    IN BOOLEAN connected
)
{
    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_WORKITEM_CONFIG   workitemConfig;
    WDFWORKITEM           hWorkItem;
    PRAWPDO_VIOSERIAL_PORT  pdoData = NULL;
    NTSTATUS              status = STATUS_SUCCESS;

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> %s\n", __FUNCTION__);
    port->HostConnected = connected;

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, RAWPDO_VIOSERIAL_PORT);
    attributes.ParentObject = WdfDevice;
    WDF_WORKITEM_CONFIG_INIT(&workitemConfig, VIOSerialPortPnpNotifyWork);

    status = WdfWorkItemCreate( &workitemConfig,
                                 &attributes,
                                 &hWorkItem);

    if (!NT_SUCCESS(status))
    {
       TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "WdfWorkItemCreate failed with status = 0x%08x\n", status);
       return;
    }

    pdoData = RawPdoSerialPortGetData(hWorkItem);

    pdoData->port = port;

    WdfWorkItemEnqueue(hWorkItem);
    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- %s\n", __FUNCTION__);
}
NTSTATUS AndroidUsbPipeFileObject::QueueResetPipePassiveCallback() {
  ASSERT_IRQL_LOW_OR_DISPATCH();

  // Initialize workitem
  WDF_OBJECT_ATTRIBUTES attr;
  WDF_OBJECT_ATTRIBUTES_INIT(&attr);
  WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attr, AndroidUsbWorkitemContext);
  attr.ParentObject = wdf_device();

  WDFWORKITEM wdf_work_item = NULL;
  WDF_WORKITEM_CONFIG workitem_config;
  WDF_WORKITEM_CONFIG_INIT(&workitem_config, ResetPipePassiveCallbackEntry);
  NTSTATUS status = WdfWorkItemCreate(&workitem_config,
                                      &attr,
                                      &wdf_work_item);
  ASSERT(NT_SUCCESS(status) && (NULL != wdf_work_item));
  if (!NT_SUCCESS(status))
    return status;

  // Initialize our extension to work item
  AndroidUsbWorkitemContext* context =
    GetAndroidUsbWorkitemContext(wdf_work_item);
  ASSERT(NULL != context);
  if (NULL == context) {
    WdfObjectDelete(wdf_work_item);
    return STATUS_INTERNAL_ERROR;
  }

  context->object_type = AndroidUsbWdfObjectTypeWorkitem;
  context->pipe_file_ext = this;

  // Enqueue this work item.
  WdfWorkItemEnqueue(wdf_work_item);

  return STATUS_SUCCESS;
}
VOID
VIOSerialPortCreateName(
    IN WDFDEVICE WdfDevice,
    IN PVIOSERIAL_PORT port,
    IN PPORT_BUFFER buf
    )
{
    WDF_OBJECT_ATTRIBUTES attributes;
    WDF_WORKITEM_CONFIG   workitemConfig;
    WDFWORKITEM           hWorkItem;
    PRAWPDO_VIOSERIAL_PORT  pdoData = NULL;
    NTSTATUS              status = STATUS_SUCCESS;
    size_t                length;
    PVIRTIO_CONSOLE_CONTROL cpkt;

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "--> %s\n", __FUNCTION__);
    cpkt = (PVIRTIO_CONSOLE_CONTROL)((ULONG_PTR)buf->va_buf + buf->offset);
    if (port && !port->NameString.Buffer)
    {
        length = buf->len - buf->offset - sizeof(VIRTIO_CONSOLE_CONTROL);
        port->NameString.Length = (USHORT)( length );
        port->NameString.MaximumLength = port->NameString.Length + 1;
        port->NameString.Buffer = (PCHAR)ExAllocatePoolWithTag(
                                 NonPagedPool,
                                 port->NameString.MaximumLength,
                                 VIOSERIAL_DRIVER_MEMORY_TAG
                                 );
        if (port->NameString.Buffer)
        {
           RtlCopyMemory(  port->NameString.Buffer,
                                 (PVOID)((LONG_PTR)buf->va_buf + buf->offset + sizeof(*cpkt)),
                                 length
                                 );
           port->NameString.Buffer[length] = '\0';
           TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "VIRTIO_CONSOLE_PORT_NAME name_size = %d %s\n", length, port->NameString.Buffer);
        }
        else
        {
           TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                                 "VIRTIO_CONSOLE_PORT_NAME: Unable to alloc string buffer\n"
                                 );
        }

        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, RAWPDO_VIOSERIAL_PORT);
        attributes.ParentObject = WdfDevice;
        WDF_WORKITEM_CONFIG_INIT(&workitemConfig, VIOSerialPortSymbolicNameWork);

        status = WdfWorkItemCreate( &workitemConfig,
                                 &attributes,
                                 &hWorkItem);

        if (!NT_SUCCESS(status))
        {
           TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "WdfWorkItemCreate failed with status = 0x%08x\n", status);
           return;
        }

        pdoData = RawPdoSerialPortGetData(hWorkItem);

        pdoData->port = port;

        WdfWorkItemEnqueue(hWorkItem);
    }
    else
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "VIRTIO_CONSOLE_PORT_NAME invalid id = %d\n", cpkt->id);
    }
    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_CREATE_CLOSE, "<-- %s\n", __FUNCTION__);
}
Пример #8
0
NTSTATUS
PepScheduleWorker (
    _In_ PPEP_WORK_CONTEXT WorkContext
    )

/*++

Routine Description:

    This function schedules a worker thread to process pending work requests.

Arguments:

    WorkContext - Supplies the context of the work.

Return Value:

    NTSTATUS.

--*/

{

    WDF_OBJECT_ATTRIBUTES Attributes;
    NTSTATUS Status;
    BOOLEAN Synchronous;
    WDFWORKITEM WorkItem;
    WDF_WORKITEM_CONFIG WorkItemConfiguration;

    WorkItem = NULL;
    Synchronous = FALSE;

    //
    // Create a workitem to process events.
    //

    WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
    WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&Attributes,
                                           PEP_WORK_ITEM_CONTEXT);

    Attributes.ParentObject = PepGlobalWdfDevice;

    //
    // Initialize the handler routine and create a new workitem.
    //

    WDF_WORKITEM_CONFIG_INIT(&WorkItemConfiguration, PepWorkerWrapper);

    //
    // Disable automatic serialization by the framework for the worker thread.
    // The parent device object is being serialized at device level (i.e.,
    // WdfSynchronizationScopeDevice), and the framework requires it to be
    // passive level (i.e., WdfExecutionLevelPassive) if automatic
    // synchronization is desired.
    //

    WorkItemConfiguration.AutomaticSerialization = FALSE;

    //
    // Create the work item and queue it. If the workitem cannot be created
    // for some reason, just call the worker routine synchronously.
    //

    Status = WdfWorkItemCreate(&WorkItemConfiguration,
                               &Attributes,
                               &WorkItem);

    if (!NT_SUCCESS(Status)) {
        Synchronous = TRUE;
        TraceEvents(INFO,
                    DBG_PEP,
                    "%s: Failed to allocate work item to process pending"
                    "work! Status = %!STATUS!. Will synchronously process.\n",
                    __FUNCTION__,
                    Status);
    }

    //
    // If the operation is to be performed synchronously, then directly
    // invoke the worker routine. Otherwise, queue a workitem to run the
    // worker routine.
    //

    if (Synchronous != FALSE) {
        PepProcessPendingWorkRequests();

    } else {
        TraceEvents(INFO,
                    DBG_PEP,
                    "%s: Work request scheduled to run asynchronously. "
                    "Device=%p, WorkType=%d, NotificationId=%d.\n",
                    __FUNCTION__,
                    (PVOID)WorkContext->PepInternalDevice,
                    (ULONG)WorkContext->WorkType,
                    (ULONG)WorkContext->NotificationId);

        WdfWorkItemEnqueue(WorkItem);
    }

    return STATUS_SUCCESS;
}
Пример #9
0
/**
 * @brief Allocate a workitem from or for our collection of workitems.
 * Must be called with the device lock held.
 * 
 * @param[in] Device handle to the WDFDEVICE created by FdoEvtDeviceAdd.
 * @param[in] callback only optional if this is add device doing an allocation!
 * @param[in] Param0 arbitrary context data
 * @param[in] Param1 arbitrary context data
 * @param[in] Param2 arbitrary context data
 * @param[in] Param3 arbitrary context data
 * 
 * @returns a WDFWORKITEM handle or NULL on failure.
 * 
 */
WDFWORKITEM
NewWorkItem(
    IN PUSB_FDO_CONTEXT fdoContext,
    IN PFN_WDF_WORKITEM  callback, 
    IN ULONG_PTR Param0,
    IN ULONG_PTR Param1,
    IN ULONG_PTR Param2,
    IN ULONG_PTR Param3)
{
    //
    // First try to get a workitem from our collection of them.
    //    
    WDFWORKITEM  WorkItem = (WDFWORKITEM) WdfCollectionGetFirstItem(fdoContext->FreeWorkItems);
    if (WorkItem == NULL)
    {
        //
        // ok - allocate a new one.
        //
        TraceEvents(TRACE_LEVEL_VERBOSE, TRACE_DEVICE,
            __FUNCTION__": Device %p FreeWorkItems is empty, init count %d\n",
            fdoContext->WdfDevice,
            INIT_WORK_ITEM_COUNT);

        NTSTATUS  status = STATUS_SUCCESS;
        WDF_OBJECT_ATTRIBUTES  attributes;
        WDF_WORKITEM_CONFIG  workitemConfig;

        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
            &attributes,
            USB_FDO_WORK_ITEM_CONTEXT);

        attributes.ParentObject = fdoContext->WdfDevice;

        WDF_WORKITEM_CONFIG_INIT(
            &workitemConfig,
            EvtFdoDeviceGenericWorkItem);

        status = WdfWorkItemCreate(
            &workitemConfig,
            &attributes,
            &WorkItem);

        if (!NT_SUCCESS(status)) 
        {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                __FUNCTION__": WdfWorkItemCreate error %x\n",
                status);
            return NULL;
        }
    }
    else
    {
        // note that WdfCollectionGetFirstItem doesn't remove it from the collection.
        WdfCollectionRemove(fdoContext->FreeWorkItems, WorkItem);
    }
    if (WorkItem)
    {
        // initialize it.
        PUSB_FDO_WORK_ITEM_CONTEXT  context = WorkItemGetContext(WorkItem);
        context->FdoContext = fdoContext;
        context->CallBack = callback;
        context->Params[0] = Param0;
        context->Params[1] = Param1;
        context->Params[2] = Param2;
        context->Params[3] = Param3;
    }
    return WorkItem;
}
Пример #10
0
NTSTATUS
I2COpen(
    _In_ PDEVICE_CONTEXT DeviceContext
)
/*++

Routine Description:

    This routine opens a handle to the I2C controller.

Arguments:

    DeviceContext - a pointer to the device context

Return Value:

    NTSTATUS

--*/
{
    TRACE_FUNC_ENTRY(TRACE_I2C);

    PAGED_CODE();

    NTSTATUS status;
    WDF_IO_TARGET_OPEN_PARAMS openParams;
    WDF_OBJECT_ATTRIBUTES requestAttributes;
    WDF_OBJECT_ATTRIBUTES workitemAttributes;
    WDF_WORKITEM_CONFIG workitemConfig;

    // Create the device path using the connection ID.
    DECLARE_UNICODE_STRING_SIZE(DevicePath, RESOURCE_HUB_PATH_SIZE);

    RESOURCE_HUB_CREATE_PATH_FROM_ID(
        &DevicePath,
        DeviceContext->I2CConnectionId.LowPart,
        DeviceContext->I2CConnectionId.HighPart);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_I2C,
        "Opening handle to I2C target via %wZ", &DevicePath);

    status = WdfIoTargetCreate(DeviceContext->Device, WDF_NO_OBJECT_ATTRIBUTES, &DeviceContext->I2CIoTarget);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
            "WdfIoTargetCreate failed - %!STATUS!", status);
        goto Exit;
    }

    // Open a handle to the I2C controller.
    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
        &openParams,
        &DevicePath,
        (GENERIC_READ | GENERIC_WRITE));

    openParams.ShareAccess = 0;
    openParams.CreateDisposition = FILE_OPEN;
    openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;

    status = WdfIoTargetOpen(DeviceContext->I2CIoTarget, &openParams);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
            "Failed to open I2C I/O target - %!STATUS!", status);
        goto Exit;
    }

    // Create a WDFMEMORY object. Do call WdfMemoryAssignBuffer before use it,
    status = WdfMemoryCreatePreallocated(
        WDF_NO_OBJECT_ATTRIBUTES,
        static_cast<PVOID>(&status), // initial value does not matter
        sizeof(status),
        &DeviceContext->I2CMemory);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
            "WdfMemoryCreatePreallocated failed with status %!STATUS!", status);
        goto Exit;
    }

    WDF_OBJECT_ATTRIBUTES_INIT(&requestAttributes);
    requestAttributes.ParentObject = DeviceContext->I2CIoTarget;

    for (ULONG i = 0; i < I2CRequestSourceMax; i++)
    {
        status = WdfRequestCreate(&requestAttributes, DeviceContext->I2CIoTarget, &DeviceContext->OutgoingRequests[i]);
        if (!NT_SUCCESS(status))
        {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
                "WdfRequestCreate failed with status %!STATUS!", status);
            goto Exit;
        }
    }

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&workitemAttributes, WORKITEM_CONTEXT);
    workitemAttributes.ParentObject = DeviceContext->I2CIoTarget;

    WDF_WORKITEM_CONFIG_INIT(&workitemConfig, EvtWorkItemGetStatus);
    status = WdfWorkItemCreate(&workitemConfig, &workitemAttributes, &DeviceContext->I2CWorkItemGetStatus);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
            "WdfWorkItemCreate failed with status %!STATUS!", status);
        goto Exit;
    }

    WDF_WORKITEM_CONFIG_INIT(&workitemConfig, EvtWorkItemGetControl);
    status = WdfWorkItemCreate(&workitemConfig, &workitemAttributes, &DeviceContext->I2CWorkItemGetControl);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_I2C,
            "WdfWorkItemCreate failed with status %!STATUS!", status);
        goto Exit;
    }

Exit:
    TRACE_FUNC_EXIT(TRACE_I2C);
    return status;
}
Пример #11
0
VOID
kmdf1394_BusResetRoutine(
                         IN PVOID    Context)
{
    WDFDEVICE device= Context;
    PDEVICE_EXTENSION deviceExtension = GetDeviceContext(device);
    NTSTATUS ntStatus    = STATUS_SUCCESS;
    WDFREQUEST request;
    WDF_WORKITEM_CONFIG workItemConfig;
    WDFWORKITEM workItem;
    WDF_OBJECT_ATTRIBUTES attributes;

    ENTER("kmdf1394_BusResetRoutine");

    TRACE(TL_TRACE, ("Context = 0x%x\n", Context));

    WDF_WORKITEM_CONFIG_INIT (&workItemConfig, kmdf1394_BusResetRoutineWorkItem);

    WDF_OBJECT_ATTRIBUTES_INIT (&attributes);
    attributes.ParentObject = device;

    ntStatus = WdfWorkItemCreate( &workItemConfig,
        &attributes,
        &workItem);
    if (!NT_SUCCESS (ntStatus)) 
    {
        TRACE(TL_ERROR, ("Failed to create workitem %x\n", ntStatus));
        return;
    }

    //
    // Execute this work item.
    //
    WdfWorkItemEnqueue (workItem);

    //
    // If we have any bus reset notify irps, then nows the
    // time to complete them.
    //
    WHILE (TRUE)
    {
        ntStatus = WdfIoQueueRetrieveNextRequest (
            deviceExtension->BusResetRequestsQueue,
            &request);
        if(NT_SUCCESS (ntStatus))
        {
            TRACE(TL_TRACE, ("Completing BusReset Request = 0x%p\n", request));
            WdfRequestCompleteWithInformation (request, STATUS_SUCCESS, 0);
            // continue;
        }
        else if (STATUS_NO_MORE_ENTRIES == ntStatus)
        {
            TRACE (TL_TRACE, ("Request Queue is empty.\n"));
            break;
        }
        else 
        {
            ASSERTMSG (
                "WdfIoQueueRetrieveNextRequest failed", 
                ntStatus);
            break;
        }
    }

    EXIT("kmdf1394_BusResetRoutine", ntStatus);
} // kmdf1394_BusResetRoutine
Пример #12
0
NTSTATUS
SimSensorDriverDeviceAdd (
    WDFDRIVER Driver,
    PWDFDEVICE_INIT DeviceInit
    )

/*++

Routine Description:

    EvtDriverDeviceAdd is called by the framework in response to AddDevice call
    from the PnP manager. A WDF device object is created and initialized to
    represent a new instance of the battery device.

Arguments:

    Driver - Supplies a handle to the WDF Driver object.

    DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT
        structure.

Return Value:

    NTSTATUS

--*/

{

    WDF_OBJECT_ATTRIBUTES DeviceAttributes;
    WDFDEVICE DeviceHandle;
    PFDO_DATA DevExt;
    BOOLEAN LockHeld;
    WDF_IO_QUEUE_CONFIG PendingRequestQueueConfig;
    WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
    WDFQUEUE Queue;
    WDF_IO_QUEUE_CONFIG QueueConfig;
    NTSTATUS Status;
    WDF_OBJECT_ATTRIBUTES WorkitemAttributes;
    WDF_WORKITEM_CONFIG WorkitemConfig;

    UNREFERENCED_PARAMETER(Driver);

    DebugEnter();
    PAGED_CODE();

    LockHeld = FALSE;

    //
    // Initialize attributes and a context area for the device object.
    //

    WDF_OBJECT_ATTRIBUTES_INIT(&DeviceAttributes);
    WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&DeviceAttributes, FDO_DATA);

    //
    // Initailize power callbacks
    //

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
    PnpPowerCallbacks.EvtDeviceD0Entry = SimSensorDeviceD0Entry;
    PnpPowerCallbacks.EvtDeviceD0Exit = SimSensorDeviceD0Exit;
    PnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend =
        SimSensorSelfManagedIoSuspend;

    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);

    //
    // Create a framework device object.  This call will in turn create
    // a WDM device object, attach to the lower stack, and set the
    // appropriate flags and attributes.
    //

    Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &DeviceHandle);
    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                   "WdfDeviceCreate() Failed. 0x%x\n",
                   Status);

        goto DriverDeviceAddEnd;
    }

    DevExt = GetDeviceExtension(DeviceHandle);

    //
    // Configure a default queue for IO requests. This queue processes requests
    // to read the sensor state.
    //

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig,
                                           WdfIoQueueDispatchParallel);

    QueueConfig.EvtIoDeviceControl = SimSensorIoDeviceControl;

    //
    // The system uses IoInternalDeviceControl requests to communicate with the
    // ACPI driver on the device stack. For proper operation of thermal zones,
    // these requests must be forwarded unless the driver knows how to handle
    // them.
    //

    QueueConfig.EvtIoInternalDeviceControl = SimSensorIoInternalDeviceControl;
    Status = WdfIoQueueCreate(DeviceHandle,
                              &QueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &Queue);

    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                   "WdfIoQueueCreate() (Default) Failed.  0x%x\n",
                   Status);

        goto DriverDeviceAddEnd;
    }

    //
    // Configure a manual dispatch queue for pending requests. This queue
    // stores requests to read the sensor state which can't be retired
    // immediately.
    //

    WDF_IO_QUEUE_CONFIG_INIT(&PendingRequestQueueConfig,
                             WdfIoQueueDispatchManual);


    Status = WdfIoQueueCreate(DeviceHandle,
                              &PendingRequestQueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &DevExt->PendingRequestQueue);

    PendingRequestQueueConfig.EvtIoStop = SimSensorQueueIoStop;
    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                "WdfIoQueueCreate() (Pending) Failed. 0x%x\n",
                Status);

        goto DriverDeviceAddEnd;
    }


    //
    // Configure a workitem to process the simulated interrupt.
    //

    WDF_OBJECT_ATTRIBUTES_INIT(&WorkitemAttributes);
    WorkitemAttributes.ParentObject = DeviceHandle;
    WDF_WORKITEM_CONFIG_INIT(&WorkitemConfig,
                             SimSensorTemperatureInterruptWorker);

    Status = WdfWorkItemCreate(&WorkitemConfig,
                               &WorkitemAttributes,
                               &DevExt->InterruptWorker);

    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                   "WdfWorkItemCreate() Failed. 0x%x\n",
                   Status);

        goto DriverDeviceAddEnd;
    }

    //
    // Create the request queue waitlock.
    //

    Status = WdfWaitLockCreate(NULL, &DevExt->QueueLock);
    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                   "WdfWaitLockCreate() Failed. Status 0x%x\n",
                   Status);

        goto DriverDeviceAddEnd;
    }

    //
    // Initilize the simulated sensor hardware.
    //

    DevExt->Sensor.LowerBound = 0;
    DevExt->Sensor.UpperBound = (ULONG)-1;
    DevExt->Sensor.Temperature = VIRTUAL_SENSOR_RESET_TEMPERATURE;
    Status = WdfWaitLockCreate(NULL, &DevExt->Sensor.Lock);
    if (!NT_SUCCESS(Status)) {
        DebugPrint(SIMSENSOR_ERROR,
                   "WdfWaitLockCreate() Failed. 0x%x\n",
                   Status);

        goto DriverDeviceAddEnd;
    }

DriverDeviceAddEnd:

    DebugExitStatus(Status);
    return Status;
}