632 строки
17 KiB
C
632 строки
17 KiB
C
/*++
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name: Device.c
|
|
|
|
Abstract: Radio manager device main file.
|
|
|
|
History:
|
|
4/9/2009: Created by senxiang
|
|
--*/
|
|
#include <initguid.h>
|
|
#include "device.h"
|
|
#include "IsrDpc.h"
|
|
#include "trace.h"
|
|
#ifdef EVENT_TRACING
|
|
#include "device.tmh"
|
|
#endif
|
|
|
|
#pragma alloc_text (PAGE, PCIEEvtDeviceAdd)
|
|
#pragma alloc_text (PAGE, PCIEEvtDevicePrepareHardware)
|
|
#pragma alloc_text (PAGE, PCIEEvtDeviceReleaseHardware)
|
|
#pragma alloc_text (PAGE, PCIEEvtDeviceD0Entry)
|
|
#pragma alloc_text (PAGE, PCIEEvtDeviceD0Exit)
|
|
#pragma alloc_text (PAGE, PCIESetIdleAndWakeSettings)
|
|
#pragma alloc_text (PAGE, PCIEInitializeDeviceExtension)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EvtDeviceAdd is called by the framework in response to AddDevice
|
|
call from the PnP manager. Here the driver should register all the
|
|
PNP, power and Io callbacks, register interfaces and allocate other
|
|
software resources required by the Device. The driver can query
|
|
any interfaces or get the config space information from the bus driver
|
|
but cannot access hardware registers or initialize the Device.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
NTSTATUS PCIEEvtDeviceAdd(
|
|
IN WDFDRIVER Driver,
|
|
IN PWDFDEVICE_INIT DeviceInit
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
|
|
WDF_OBJECT_ATTRIBUTES Attributes;
|
|
WDFDEVICE Device;
|
|
PDEVICE_EXTENSION pDevExt = NULL;
|
|
UNICODE_STRING DeviceName;
|
|
UNICODE_STRING DeviceSymbolLink;
|
|
|
|
UNREFERENCED_PARAMETER(Driver);
|
|
|
|
PAGED_CODE();
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"--> PCIEEvtDeviceAdd \n");
|
|
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
|
|
|
|
//
|
|
// Zero out the PnpPowerCallbacks structure.
|
|
//
|
|
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
|
|
|
|
// Set Callbacks for any of the functions we are interested in.
|
|
// If no callback is set, Framework will take the default action
|
|
// by itself.
|
|
//
|
|
PnpPowerCallbacks.EvtDevicePrepareHardware = PCIEEvtDevicePrepareHardware;
|
|
PnpPowerCallbacks.EvtDeviceReleaseHardware = PCIEEvtDeviceReleaseHardware;
|
|
|
|
//
|
|
// These two callbacks set up and tear down hardware state that must be
|
|
// done every time the Device moves in and out of the D0-working state.
|
|
//
|
|
PnpPowerCallbacks.EvtDeviceQueryRemove = PCIEEvtDeviceQueryStopRemove;
|
|
PnpPowerCallbacks.EvtDeviceQueryStop = PCIEEvtDeviceQueryStopRemove;
|
|
PnpPowerCallbacks.EvtDeviceD0Entry = PCIEEvtDeviceD0Entry;
|
|
PnpPowerCallbacks.EvtDeviceD0Exit = PCIEEvtDeviceD0Exit;
|
|
|
|
//
|
|
// Register the PnP Callbacks..
|
|
//
|
|
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);
|
|
|
|
//
|
|
// Initialize Fdo Attributes.
|
|
//
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attributes, DEVICE_EXTENSION);
|
|
//
|
|
// By opting for SynchronizationScopeDevice, we tell the framework to
|
|
// synchronize callbacks events of all the objects directly associated
|
|
// with the Device. In this driver, we will associate queues and
|
|
// and DpcForIsr. By doing that we don't have to worrry about synchronizing
|
|
// access to Device-context by Io Events and DpcForIsr because they would
|
|
// not concurrently ever. Framework will serialize them by using an
|
|
// internal Device-lock.
|
|
//
|
|
Attributes.SynchronizationScope = WdfSynchronizationScopeNone;
|
|
RtlInitUnicodeString(&DeviceName, SORA_DEVICE_NAME);
|
|
RtlInitUnicodeString(&DeviceSymbolLink, SORA_DEVICE_SYMBOL_NAME);
|
|
|
|
do{
|
|
Status = WdfDeviceInitAssignName(
|
|
DeviceInit,
|
|
&DeviceName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WdfDeviceCreate( &DeviceInit, &Attributes, &Device );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WdfDeviceCreateSymbolicLink(Device, &DeviceSymbolLink);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WdfDeviceCreateDeviceInterface( Device, (LPGUID) &GUID_PCIE_INTERFACE, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
pDevExt = PCIEGetDeviceContext(Device);
|
|
pDevExt->Device = Device;
|
|
|
|
//
|
|
// set idle may cause the PCI-E bus stop responsing after the idle time, so mark this setting here
|
|
//
|
|
//PCIESetIdleAndWakeSettings(pDevExt);
|
|
|
|
Status = PCIEInitializeDeviceExtension(pDevExt);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TraceEvents(
|
|
TRACE_LEVEL_FATAL,
|
|
DBG_INIT,
|
|
"PCIEInitializeDeviceExtension Failed with %08X\n", Status);
|
|
break;
|
|
}
|
|
|
|
} while(FALSE);
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"<-- PCIEEvtDeviceAdd \n");
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine is called by EvtDeviceAdd. Here the device context is
|
|
initialized and all the software resources required by the device is
|
|
allocated.
|
|
|
|
Arguments:
|
|
|
|
pDevExt Pointer to the Device Extension
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
NTSTATUS PCIEInitializeDeviceExtension(IN PDEVICE_EXTENSION pDevExt)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
WDF_IO_QUEUE_CONFIG QueueConfig;
|
|
WDF_IO_QUEUE_CONFIG InternalQueueConfig;
|
|
|
|
PAGED_CODE();
|
|
|
|
//RtlZeroMemory(pDevExt, sizeof(DEVICE_EXTENSION));
|
|
|
|
WDF_IO_QUEUE_CONFIG_INIT ( &QueueConfig, WdfIoQueueDispatchSequential);
|
|
QueueConfig.EvtIoDeviceControl = PCIEDeviceIoCtrl;
|
|
do
|
|
{
|
|
Status = WdfIoQueueCreate(
|
|
pDevExt->Device,
|
|
&QueueConfig,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&pDevExt->IOCtrlQueue );
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = WdfDeviceConfigureRequestDispatching(
|
|
pDevExt->Device,
|
|
pDevExt->IOCtrlQueue,
|
|
WdfRequestTypeDeviceControl);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = PCIEInterruptCreate(pDevExt);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
} while(FALSE);
|
|
|
|
pDevExt->RadioManagerRef = 0;
|
|
pDevExt->RadioMask = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
Routine Description:
|
|
|
|
Called by EvtDeviceAdd to set the idle and wait-wake policy. Registering this policy
|
|
causes Power Management Tab to show up in the Device manager. By default these
|
|
options are enabled and the user is provided control to change the settings.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Failure Status is returned if the Device is not capable of suspending
|
|
or wait-waking the machine by an external event. Framework checks the
|
|
capability information reported by the bus driver to decide whether the Device is
|
|
capable of waking the machine.
|
|
|
|
--*/
|
|
NTSTATUS
|
|
PCIESetIdleAndWakeSettings(IN PDEVICE_EXTENSION FdoData)
|
|
{
|
|
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings;
|
|
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS WakeSettings;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"--> PCIESetIdleAndWakeSettings \n");
|
|
do
|
|
{
|
|
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings, IdleCanWakeFromS0);
|
|
IdleSettings.IdleTimeout = DEVICE_IDLE_TIMEOUT;
|
|
|
|
Status = WdfDeviceAssignS0IdleSettings(FdoData->Device, &IdleSettings);
|
|
if ( !NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
|
|
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&WakeSettings);
|
|
Status = WdfDeviceAssignSxWakeSettings(FdoData->Device, &WakeSettings);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
} while (FALSE);
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"<-- PCIESetIdleAndWakeSettings \n");
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs whatever initialization is needed to setup the Device, setting up
|
|
a DMA channel or mapping any I/O port resources. This will only be called
|
|
as a Device starts or restarts, not every time the Device moves into the D0
|
|
state. Consequently, most hardware initialization belongs elsewhere.
|
|
|
|
Arguments:
|
|
|
|
Device - A handle to the WDFDEVICE
|
|
|
|
Resources - The raw PnP resources associated with the Device. Most of the
|
|
time, these aren't useful for a PCI Device.
|
|
|
|
ResourcesTranslated - The translated PnP resources associated with the
|
|
Device. This is what is important to a PCI Device.
|
|
|
|
Return Value:
|
|
|
|
NT Status code - failure will result in the Device stack being torn down
|
|
|
|
--*/
|
|
|
|
NTSTATUS
|
|
PCIEEvtDevicePrepareHardware (
|
|
WDFDEVICE Device,
|
|
WDFCMRESLIST Resources,
|
|
WDFCMRESLIST ResourcesTranslated
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
HRESULT hRes;
|
|
PDEVICE_EXTENSION pDevExt;
|
|
ULONG Index, Count = 0;
|
|
ULONG RegPhyAddressLength = 0;
|
|
PHYSICAL_ADDRESS RegPhyAddressBase = {0, 0};
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResDesc;
|
|
|
|
UNREFERENCED_PARAMETER(Resources);
|
|
|
|
PAGED_CODE();
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"--> PCIEEvtDevicePrepareHardware \n");
|
|
|
|
do
|
|
{
|
|
pDevExt = PCIEGetDeviceContext(Device);
|
|
|
|
INIT_SORA_THREAD(
|
|
pDevExt->RadioUpdateThread,
|
|
RadioUpdator,
|
|
pDevExt,
|
|
SYNCHRONIZE | GENERIC_ALL | STANDARD_RIGHTS_ALL,
|
|
CORE_ANY);
|
|
|
|
for (Index=0; Index < WdfCmResourceListGetCount(ResourcesTranslated); Index++)
|
|
{
|
|
pResDesc = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);
|
|
if (!pResDesc)
|
|
{
|
|
Status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
break;
|
|
}
|
|
switch(pResDesc->Type)
|
|
{
|
|
case CmResourceTypeMemory:
|
|
RegPhyAddressLength = pResDesc->u.Memory.Length;
|
|
RegPhyAddressBase = pResDesc->u.Memory.Start;
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"Hardware Memory Resource %08x%08x, Length = %08x\n",
|
|
RegPhyAddressBase.HighPart,
|
|
RegPhyAddressBase.LowPart,
|
|
RegPhyAddressLength
|
|
);
|
|
Count++;
|
|
break;
|
|
default: //Currently ignore other resources
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (1 != Count)
|
|
{
|
|
Status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
break;
|
|
}
|
|
|
|
hRes = SoraInitRadioManager2(
|
|
RegPhyAddressBase,
|
|
RegPhyAddressLength,
|
|
&pDevExt->RadioManager);
|
|
if (FAILED(hRes))
|
|
{
|
|
TraceEvents(
|
|
TRACE_LEVEL_ERROR,
|
|
DBG_INIT,
|
|
"SoraInitRadioManager failed \n");
|
|
Status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
|
break;
|
|
}
|
|
|
|
pDevExt->ResourceMonitor = AllocateProcessObjectMonitor();
|
|
if (!pDevExt) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
} while(FALSE);
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"<-- PCIEEvtDevicePrepareHardware \n");
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID RadioUpdator(PVOID pVoid)
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = SORA_THREAD_CONTEXT_PTR(DEVICE_EXTENSION, pVoid);
|
|
LARGE_INTEGER delay;
|
|
PSORA_RADIO_MANAGER pRadioMgr = &pDevExt->RadioManager;
|
|
|
|
delay.QuadPart = WDF_REL_TIMEOUT_IN_MS(RADIO_UPDATE_INTERVAL);
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"--> RadioUpdator Started \n");
|
|
do
|
|
{
|
|
ULONG i;
|
|
for ( i = 0; i < pRadioMgr->__radio_count; i++)
|
|
{
|
|
SORA_HW_READ_RADIO_POWER_STATUS(&pRadioMgr->__radio_pool[i]);
|
|
}
|
|
KdPrint(("Radio status Updator running ....\n"));
|
|
KeDelayExecutionThread(KernelMode, TRUE, &delay);
|
|
} while(!IS_SORA_THREAD_NEED_TERMINATE(pVoid));
|
|
|
|
SORA_THREAD_STOPPED(pVoid);
|
|
|
|
TraceEvents(
|
|
TRACE_LEVEL_INFORMATION,
|
|
DBG_INIT,
|
|
"--> RadioUpdator Stopped \n");
|
|
KdPrint(("Radio status Updator stopped \n"));
|
|
PsTerminateSystemThread(STATUS_SUCCESS);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmap the resources that were mapped in PCIEEvtDevicePrepareHardware.
|
|
This will only be called when the Device stopped for resource rebalance,
|
|
surprise-removed or query-removed.
|
|
|
|
Arguments:
|
|
|
|
Device - A handle to the WDFDEVICE
|
|
|
|
ResourcesTranslated - The translated PnP resources associated with the
|
|
Device. This is what is important to a PCI Device.
|
|
|
|
Return Value:
|
|
|
|
NT Status code - failure will result in the Device stack being torn down
|
|
|
|
--*/
|
|
|
|
NTSTATUS PCIEEvtDeviceReleaseHardware(
|
|
IN WDFDEVICE Device,
|
|
IN WDFCMRESLIST ResourcesTranslated
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSORA_RADIO_MANAGER pRadioManager = NULL;
|
|
HRESULT hRes;
|
|
INT Retry = 0;
|
|
LARGE_INTEGER delay;
|
|
|
|
|
|
delay.QuadPart = WDF_REL_TIMEOUT_IN_MS(CLEANUP_RETRY_INTERVAL);
|
|
|
|
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
|
|
|
PAGED_CODE();
|
|
|
|
pDevExt = PCIEGetDeviceContext(Device);
|
|
pRadioManager = &pDevExt->RadioManager;
|
|
|
|
if (pDevExt->ResourceMonitor) {
|
|
ReleaseProcessObjectMonitor(pDevExt->ResourceMonitor);
|
|
pDevExt->ResourceMonitor = NULL;
|
|
}
|
|
|
|
STOP_SORA_THREAD(pDevExt->RadioUpdateThread);
|
|
do
|
|
{
|
|
hRes = SoraCleanupRadioManager2(&pDevExt->RadioManager);
|
|
Retry++;
|
|
if (Retry > 1)
|
|
{
|
|
KeDelayExecutionThread(KernelMode, TRUE, &delay);
|
|
}
|
|
} while (FAILED(hRes) && Retry < CLEANUP_RETRY_TIMES);
|
|
|
|
if (FAILED(hRes))
|
|
{
|
|
Status = STATUS_DEVICE_BUSY;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares the Device for use. It is called whenever the Device
|
|
enters the D0 state, which happens when the Device is started, when it is
|
|
restarted, and when it has been powered off.
|
|
|
|
Note that interrupts will not be enabled at the time that this is called.
|
|
They will be enabled after this callback completes.
|
|
|
|
This function is not marked pageable because this function is in the
|
|
Device power up path. When a function is marked pagable and the code
|
|
section is paged out, it will generate a page fault which could impact
|
|
the fast resume behavior because the client driver will have to wait
|
|
until the system drivers can service this page fault.
|
|
|
|
Arguments:
|
|
|
|
Device - The handle to the WDF Device object
|
|
|
|
PreviousState - The state the Device was in before this callback was invoked.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
Success implies that the Device can be used.
|
|
|
|
Failure will result in the Device stack being torn down.
|
|
|
|
--*/
|
|
NTSTATUS PCIEEvtDeviceD0Entry(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE PreviousState
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION pDevExt;
|
|
NTSTATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(PreviousState);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine undoes anything done in PCIEEvtDeviceD0Entry. It is called
|
|
whenever the Device leaves the D0 state, which happens when the Device
|
|
is stopped, when it is removed, and when it is powered off.
|
|
|
|
The Device is still in D0 when this callback is invoked, which means that
|
|
the driver can still touch hardware in this routine.
|
|
|
|
Note that interrupts have already been disabled by the time that this
|
|
callback is invoked.
|
|
|
|
Arguments:
|
|
|
|
Device - The handle to the WDF Device object
|
|
|
|
TargetState - The state the Device will go to when this callback completes.
|
|
|
|
Return Value:
|
|
|
|
Success implies that the Device can be used. Failure will result in the
|
|
Device stack being torn down.
|
|
|
|
--*/
|
|
NTSTATUS PCIEEvtDeviceD0Exit(
|
|
IN WDFDEVICE Device,
|
|
IN WDF_POWER_DEVICE_STATE TargetState
|
|
)
|
|
{
|
|
PDEVICE_EXTENSION pDevExt;
|
|
|
|
PAGED_CODE();
|
|
|
|
pDevExt = PCIEGetDeviceContext(Device);
|
|
|
|
switch (TargetState) {
|
|
case WdfPowerDeviceD1:
|
|
case WdfPowerDeviceD2:
|
|
case WdfPowerDeviceD3:
|
|
|
|
//
|
|
// Fill in any code to save hardware state here.
|
|
//
|
|
|
|
//
|
|
// Fill in any code to put the Device in a low-power state here.
|
|
//
|
|
break;
|
|
|
|
case WdfPowerDevicePrepareForHibernation:
|
|
|
|
//
|
|
// Fill in any code to save hardware state here. Do not put in any
|
|
// code to shut the Device off. If this Device cannot support being
|
|
// in the paging path (or being a parent or grandparent of a paging
|
|
// path Device) then this whole case can be deleted.
|
|
//
|
|
|
|
break;
|
|
|
|
case WdfPowerDeviceD3Final:
|
|
default:
|
|
|
|
//
|
|
// Reset the hardware, as we're shutting down for the last time.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS PCIEEvtDeviceQueryStopRemove(IN WDFDEVICE Device) {
|
|
|
|
PDEVICE_EXTENSION pDevExt;
|
|
pDevExt = PCIEGetDeviceContext(Device);
|
|
if (pDevExt->RadioManagerRef ||
|
|
pDevExt->RadioMask)
|
|
return STATUS_DEVICE_BUSY;
|
|
return STATUS_SUCCESS;
|
|
}
|