// This routine is invoked by the framework to program the device to goto 
// D0, which is the working state. The framework invokes callback every
// time the hardware needs to be (re-)initialized.  This includes after
// IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE,
// and IRP_MN_SET_POWER-D0.
NTSTATUS
PedometerDevice::OnD0Entry(
    _In_ WDFDEVICE Device,                          // Supplies a handle to the framework device object
    _In_ WDF_POWER_DEVICE_STATE /*PreviousState*/)  // WDF_POWER_DEVICE_STATE-typed enumerator that identifies
                                                    // the device power state that the device was in before this transition to D0
{
    PPedometerDevice pDevice;
    SENSOROBJECT SensorInstance = NULL;
    ULONG SensorInstanceCount = 1;
    NTSTATUS Status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

     // Get sensor instance
    Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount);
    if (!NT_SUCCESS(Status) ||
        0 == SensorInstanceCount ||
        NULL == SensorInstance)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status);
        goto Exit;
    }

    pDevice = GetPedometerContextFromSensorInstance(SensorInstance);
    if (nullptr == pDevice)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! GetPedometerContextFromSensorInstance failed %!STATUS!", Status);
        goto Exit;
    }

    //
    // Power on sensor
    //
    pDevice->m_PoweredOn = TRUE;
    InitPropVariantFromUInt32(SensorState_Idle, 
                              &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value));

Exit:
    SENSOR_FunctionExit(Status);
    return Status;
}
// This routine is invoked by the framework to program the device to go into
// a certain Dx state. The framework invokes callback every the the device is 
// leaving the D0 state, which happens when the device is stopped, when it is 
// removed, and when it is powered off.
NTSTATUS
PedometerDevice::OnD0Exit(
    _In_ WDFDEVICE Device,                       // Supplies a handle to the framework device object
    _In_ WDF_POWER_DEVICE_STATE /*TargetState*/) // Supplies the device power state which the device will be put
                                                 // in once the callback is complete
{
    PPedometerDevice pDevice;
    SENSOROBJECT SensorInstance = NULL;
    ULONG SensorInstanceCount = 1;
    NTSTATUS Status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

    // Get sensor instance
    Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount);
    if (!NT_SUCCESS(Status) ||
        0 == SensorInstanceCount ||
        NULL == SensorInstance)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status);
        goto Exit;
    }

    pDevice = GetPedometerContextFromSensorInstance(SensorInstance);
    if (nullptr == pDevice)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! GetPedometerContextFromSensorInstance failed %!STATUS!", Status);
        goto Exit;
    }

    //
    // Power on sensor
    //
    pDevice->m_PoweredOn = FALSE;

Exit:
    SENSOR_FunctionExit(Status);
    return Status;
}
// This routine is invoked by the framework to program the device to goto 
// D0, which is the working state. The framework invokes callback every
// time the hardware needs to be (re-)initialized.  This includes after
// IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE,
// and IRP_MN_SET_POWER-D0.
NTSTATUS ActivityDevice::OnD0Entry(
    _In_ WDFDEVICE device,                          // Supplies a handle to the framework device object
    _In_ WDF_POWER_DEVICE_STATE /*PreviousState*/)  // WDF_POWER_DEVICE_STATE-typed enumerator that identifies the device
                                                    // power state that the device was in before this transition to D0.
{
    NTSTATUS status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

     // Get sensor instance
    SENSOROBJECT sensorInstance = NULL;
    ULONG sensorInstanceCount = 1;

    status = SensorsCxDeviceGetSensorList(device, &sensorInstance, &sensorInstanceCount);
    if (!NT_SUCCESS(status) || sensorInstanceCount == 0 || sensorInstance == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        TraceError("ACT %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", status);
    }
    else
    {

        PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance);
        if (nullptr == pDevice)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status);
        }
        else
        {
            // Power on sensor
            pDevice->m_PoweredOn = TRUE;
            InitPropVariantFromUInt32(SensorState_Idle, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value));
        }
    }

    SENSOR_FunctionExit(status);
    return status;
}
// This routine is invoked by the framework to program the device to go into
// a certain Dx state. The framework invokes callback every the the device is 
// leaving the D0 state, which happens when the device is stopped, when it is 
// removed, and when it is powered off.
NTSTATUS ActivityDevice::OnD0Exit(
    _In_ WDFDEVICE device,                          // Supplies a handle to the framework device object.
    _In_ WDF_POWER_DEVICE_STATE /*TargetState*/)    // Supplies the device power state which the device 
                                                    // will be put in once the callback is complete.
{
    NTSTATUS status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

    // Get sensor instance
    SENSOROBJECT sensorInstance = NULL;
    ULONG sensorInstanceCount = 1;
    status = SensorsCxDeviceGetSensorList(device, &sensorInstance, &sensorInstanceCount);
    if (!NT_SUCCESS(status) || sensorInstanceCount == 0 || sensorInstance == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        TraceError("ACT %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", status);
    }
    else
    {

        PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance);
        if (nullptr == pDevice)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status);
        }
        else
        {
            // Power off sensor
            pDevice->m_PoweredOn = FALSE;
        }
    }

    SENSOR_FunctionExit(status);
    return status;
}
// This routine is called by the framework when the PnP manager is revoking
// ownership of our resources. This may be in response to either
// IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. This routine is responsible for
// performing cleanup of resources allocated in PrepareHardware callback.
// This callback is invoked before passing  the request down to the lower driver.
// This routine will also be invoked by the framework if the prepare hardware
// callback returns a failure.
NTSTATUS
CustomSensorDevice::OnReleaseHardware(
    _In_ WDFDEVICE Device,                       // Supplies a handle to the framework device object
    _In_ WDFCMRESLIST /*ResourcesTranslated*/)   // Supplies a handle to a collection of framework
// resource objects. This collection identifies the translated
// (system-physical) hardware resources that have been assigned to the
// device. The resources appear from the CPU's point of view.
{
    PHardwareSimulator pSimulator = nullptr;
    PCustomSensorDevice pDevice = nullptr;
    SENSOROBJECT SensorInstance = nullptr;
    ULONG SensorInstanceCount = 1;    // only expect 1 sensor instance
    NTSTATUS Status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

    // Get sensor instance
    Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount);
    if (!NT_SUCCESS(Status) ||
            0 == SensorInstanceCount ||
            NULL == SensorInstance)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("CSTM %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status);
        goto Exit;
    }

    pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance);
    if (nullptr == pDevice)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status);
        goto Exit;
    }

    // Delete lock
    if (pDevice->m_Lock)
    {
        WdfObjectDelete(pDevice->m_Lock);
        pDevice->m_Lock = NULL;
    }

    // Cleanup the CO2 simulator
    pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance);
    if (nullptr == pSimulator)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        TraceError("CSTM %!FUNC! GetHardwareSimulatorContextFromInstance failed %!STATUS!", Status);
        goto Exit;
    }

    pSimulator->Cleanup();

    // Delete hardware simulator instance
    if (NULL != pDevice->m_SimulatorInstance)
    {
        WdfObjectDelete(pDevice->m_SimulatorInstance);
        pDevice->m_SimulatorInstance = NULL;
    }

    // Delete sensor instance
    if (NULL != pDevice->m_SensorInstance)
    {
        WdfObjectDelete(pDevice->m_SensorInstance);
    }

Exit:
    SENSOR_FunctionExit(Status);
    return Status;
}
// This routine is called by the framework when the PnP manager is revoking
// ownership of our resources. This may be in response to either
// IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. This routine is responsible for
// performing cleanup of resources allocated in PrepareHardware callback.
// This callback is invoked before passing  the request down to the lower driver.
// This routine will also be invoked by the framework if the prepare hardware
// callback returns a failure.
NTSTATUS ActivityDevice::OnReleaseHardware(
    _In_ WDFDEVICE device,                      // Supplies a handle to the framework device object
    _In_ WDFCMRESLIST /*ResourcesTranslated*/)  // Supplies a handle to a collection of framework
                                                // resource objects. This collection identifies the translated
                                                // (system-physical) hardware resources that have been assigned to the
                                                // device. The resources appear from the CPU's point of view.
{
    NTSTATUS status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

    // Get sensor instance
    SENSOROBJECT sensorInstance = NULL;
    ULONG sensorInstanceCount = 1;    // only expect 1 sensor instance

    status = SensorsCxDeviceGetSensorList(device, &sensorInstance, &sensorInstanceCount);
    if (!NT_SUCCESS(status) || sensorInstanceCount == 0 || sensorInstance == NULL)
    {
        status = STATUS_INVALID_PARAMETER;
        TraceError("ACT %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", status);
    }
    else
    {
        PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance);
        if (nullptr == pDevice)
        {
            status = STATUS_INVALID_PARAMETER;
            TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status);
        }
        else
        {
            // Cleanup activity simulator
            if (NULL != pDevice->m_SimulatorInstance)
            {
                PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance);
                if (nullptr != pSimulator)
                {
                    pSimulator->Deinitialize();
                }
                // Continue tearing down
                WdfObjectDelete(pDevice->m_SimulatorInstance);
                pDevice->m_SimulatorInstance = NULL;
            }

            // Close handle to history retrieval thread
            if (NULL != pDevice->m_hThread)
            {
                SetEvent(pDevice->m_ExitEvent);

                DWORD result = WaitForSingleObjectEx(pDevice->m_hThread, Act_TimeoutForHistoryThread_Ms, FALSE);
                if (WAIT_OBJECT_0 != result)
                {
                    // continue tearing down
                    status = NTSTATUS_FROM_WIN32(result);
                    TraceError("ACT %!FUNC! WaitForSingleObjectEx failed %!STATUS!", status);
                }

                CloseHandle(pDevice->m_hThread);
                pDevice->m_hThread = NULL;
            }

            // Close handle to exit event
            if (NULL != pDevice->m_ExitEvent)
            {
                CloseHandle(pDevice->m_ExitEvent);
                pDevice->m_ExitEvent = NULL;
            }

            // Delete history lock
            if (NULL != pDevice->m_HistoryLock)
            {
                WdfObjectDelete(pDevice->m_HistoryLock);
                pDevice->m_HistoryLock = NULL;
            }

            // Delete lock
            if (NULL != pDevice->m_Lock)
            {
                WdfObjectDelete(pDevice->m_Lock);
                pDevice->m_Lock = NULL;
            }

            // Delete sensor instance.
            if (NULL != pDevice->m_SensorInstance)
            {
                // Memory created by WdfMemoryCreate with m_SensorInstance as parent object will be
                // destroyed automatically when m_SensorInstance is deleted. pDevice will be no longer
                // accessible at beyond this point.
                WdfObjectDelete(pDevice->m_SensorInstance);
                pDevice = nullptr;
            }
        }
    }
 
    SENSOR_FunctionExit(status);
    return status;
}
// This routine is called by the framework when the PnP manager is revoking
// ownership of our resources. This may be in response to either
// IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. This routine is responsible for
// performing cleanup of resources allocated in PrepareHardware callback.
// This callback is invoked before passing  the request down to the lower driver.
// This routine will also be invoked by the framework if the prepare hardware
// callback returns a failure.
//
// Argument:
//      Device: IN: Supplies a handle to the framework device object
//      ResourcesTranslated: IN: Supplies a handle to a collection of framework
//          resource objects. This collection identifies the translated
//          (system-physical) hardware resources that have been assigned to the
//          device. The resources appear from the CPU's point of view.
//
// Return Value:
//      NTSTATUS code
//------------------------------------------------------------------------------
NTSTATUS
PedometerDevice::OnReleaseHardware(
    _In_ WDFDEVICE Device,                      // Supplies a handle to the framework device object
    _In_ WDFCMRESLIST /*ResourcesTranslated*/)  // Supplies a handle to a collection of framework
                                                // resource objects. This collection identifies the translated
                                                // (system-physical) hardware resources that have been assigned to the
                                                // device. The resources appear from the CPU's point of view.
{
    PHardwareSimulator pSimulator = nullptr;
    PPedometerDevice pDevice = nullptr;
    SENSOROBJECT SensorInstance = nullptr;
    ULONG SensorInstanceCount = 1;    // only expect 1 sensor instance
    NTSTATUS Status = STATUS_SUCCESS;

    SENSOR_FunctionEnter();

    // Get sensor instance
    Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount);
    if (!NT_SUCCESS(Status) ||
        0 == SensorInstanceCount ||
        NULL == SensorInstance)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status);
        goto Exit;
    }

    pDevice = GetPedometerContextFromSensorInstance(SensorInstance);
    if (nullptr == pDevice)
    {
        Status = STATUS_INVALID_PARAMETER;
        TraceError("PED %!FUNC! GetPedometerContextFromSensorInstance failed %!STATUS!", Status);
        goto Exit;
    }

    pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance);
    if (nullptr == pSimulator)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        TraceError("PED %!FUNC! GetHardwareSimulatorContextFromInstance failed %!STATUS!", Status);
        goto Exit;
    }

    // Close handle to history retrieval thread
    if (NULL != pDevice->m_hThread)
    {
        pSimulator->SignalReadCancellation();

        DWORD result = WaitForSingleObjectEx(pDevice->m_hThread, Pedometer_TimeoutForHistoryThread_Ms, FALSE);
        if (WAIT_OBJECT_0 != result)
        {
            TraceError("PED %!FUNC! WaitForSingleObjectEx failed with error %d", result);
            goto Exit;
        }

        CloseHandle(pDevice->m_hThread);
        pDevice->m_hThread = NULL;
    }


    // Delete lock
    if (NULL != pDevice->m_Lock)
    {
        WdfObjectDelete(pDevice->m_Lock);
        pDevice->m_Lock = NULL;
    }    
    
    // Cleanup the pedometer simulator
    pSimulator->Cleanup();

    // Delete hardware simulator instance
    if (NULL != pDevice->m_SimulatorInstance)
    {
        WdfObjectDelete(pDevice->m_SimulatorInstance);
        pDevice->m_SimulatorInstance = NULL;
    }

    // Delete sensor instance
    if (NULL != pDevice->m_SensorInstance)
    {
        WdfObjectDelete(pDevice->m_SensorInstance);
    }
  
Exit:
    SENSOR_FunctionExit(Status);
    return Status;
}