/*++
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Module Name:
Device.c
Abstract:
USB device driver for OSR USB-FX2 Learning Kit
Environment:
Kernel mode only
--*/
#include <osrusbfx2.h>
#if defined(EVENT_TRACING)
#include "device.tmh"
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, OsrFxEvtDeviceAdd)
#pragma alloc_text(PAGE, OsrFxEvtDevicePrepareHardware)
#pragma alloc_text(PAGE, OsrFxEvtDeviceD0Exit)
#pragma alloc_text(PAGE, SelectInterfaces)
#pragma alloc_text(PAGE, OsrFxSetPowerPolicy)
#pragma alloc_text(PAGE, OsrFxReadFdoRegistryKeyValue)
#endif
NTSTATUS
OsrFxEvtDeviceAdd(
__in WDFDRIVER Driver,
__in PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
EvtDeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. We create and initialize a device object to
represent a new instance of the device. All the software resources
should be allocated in this callback.
Arguments:
Driver - Handle to a framework driver object created in DriverEntry
DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
Return Value:
NTSTATUS
--*/
{
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES attributes;
NTSTATUS status;
WDFDEVICE device;
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
PDEVICE_CONTEXT pDevContext;
WDFQUEUE queue;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,"--> OsrFxEvtDeviceAdd routine\n");
//
// Initialize the pnpPowerCallbacks structure. Callback events for PNP
// and Power are specified here. If you don't supply any callbacks,
// the Framework will take appropriate default actions based on whether
// DeviceInit is initialized to be an FDO, a PDO or a filter device
// object.
//
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
//
// For usb devices, PrepareHardware callback is the to place select the
// interface and configure the device.
//
pnpPowerCallbacks.EvtDevicePrepareHardware = OsrFxEvtDevicePrepareHardware;
//
// These two callbacks start and stop the wdfusb pipe continuous reader
// as we go in and out of the D0-working state.
//
pnpPowerCallbacks.EvtDeviceD0Entry = OsrFxEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = OsrFxEvtDeviceD0Exit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
//
// Now specify the size of device extension where we track per device
// context.DeviceInit is completely initialized. So call the framework
// to create the device and attach it to the lower stack.
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceCreate failed with Status code %!STATUS!\n", status);
return status;
}
//
// Get the DeviceObject context by using accessor function specified in
// the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for DEVICE_CONTEXT.
//
pDevContext = GetDeviceContext(device);
//
// Tell the framework to set the SurpriseRemovalOK in the DeviceCaps so
// that you don't get the popup in usermode (on Win2K) when you surprise
// remove the device.
//
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
pnpCaps.SurpriseRemovalOK = WdfTrue;
WdfDeviceSetPnpCapabilities(device, &pnpCaps);
//
// Create a parallel default queue and register an event callback to
// receive ioctl requests. We will create separate queues for
// handling read and write requests. All other requests will be
// completed with error status automatically by the framework.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
WdfIoQueueDispatchParallel);
ioQueueConfig.EvtIoDeviceControl = OsrFxEvtIoDeviceControl;
status = WdfIoQueueCreate(device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue);// pointer to default queue
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfIoQueueCreate failed %!STATUS!\n", status);
return status;
}
//
// We will create a separate sequential queue and configure it
// to receive read requests. We also need to register a EvtIoStop
// handler so that we can acknowledge requests that are pending
// at the target driver.
//
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential);
ioQueueConfig.EvtIoRead = OsrFxEvtIoRead;
ioQueueConfig.EvtIoStop = OsrFxEvtIoStop;
status = WdfIoQueueCreate(
device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue // queue handle
);
if (!NT_SUCCESS (status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfIoQueueCreate failed 0x%x\n", status);
return status;
}
status = WdfDeviceConfigureRequestDispatching(
device,
queue,
WdfRequestTypeRead);
if(!NT_SUCCESS (status)){
ASSERT(NT_SUCCESS(status));
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceConfigureRequestDispatching failed 0x%x\n", status);
return status;
}
//
// We will create another sequential queue and configure it
// to receive write requests.
//
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchSequential);
ioQueueConfig.EvtIoWrite = OsrFxEvtIoWrite;
ioQueueConfig.EvtIoStop = OsrFxEvtIoStop;
status = WdfIoQueueCreate(
device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&queue // queue handle
);
if (!NT_SUCCESS (status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfIoQueueCreate failed 0x%x\n", status);
return status;
}
status = WdfDeviceConfigureRequestDispatching(
device,
queue,
WdfRequestTypeWrite);
if(!NT_SUCCESS (status)){
ASSERT(NT_SUCCESS(status));
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceConfigureRequestDispatching failed 0x%x\n", status);
return status;
}
//
// Register a manual I/O queue for handling Interrupt Message Read Requests.
// This queue will be used for storing Requests that need to wait for an
// interrupt to occur before they can be completed.
//
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchManual);
//
// This queue is used for requests that dont directly access the device. The
// requests in this queue are serviced only when the device is in a fully
// powered state and sends an interrupt. So we can use a non-power managed
// queue to park the requests since we dont care whether the device is idle
// or fully powered up.
//
ioQueueConfig.PowerManaged = WdfFalse;
status = WdfIoQueueCreate(device,
&ioQueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&pDevContext->InterruptMsgQueue
);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfIoQueueCreate failed 0x%x\n", status);
return status;
}
//
// Register a device interface so that app can find our device and talk to it.
//
status = WdfDeviceCreateDeviceInterface(device,
(LPGUID) &GUID_DEVINTERFACE_OSRUSBFX2,
NULL);// Reference String
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status);
return status;
}
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- OsrFxEvtDeviceAdd\n");
return status;
}
NTSTATUS
OsrFxEvtDevicePrepareHardware(
__in WDFDEVICE Device,
__in WDFCMRESLIST ResourceList,
__in WDFCMRESLIST ResourceListTranslated
)
/*++
Routine Description:
In this callback, the driver does whatever is necessary to make the
hardware ready to use. In the case of a USB device, this involves
reading and selecting descriptors.
Arguments:
Device - handle to a device
Return Value:
NT status value
--*/
{
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
WDF_USB_DEVICE_INFORMATION deviceInfo;
ULONG waitWakeEnable;
UNREFERENCED_PARAMETER(ResourceList);
UNREFERENCED_PARAMETER(ResourceListTranslated);
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> EvtDevicePrepareHardware\n");
pDeviceContext = GetDeviceContext(Device);
//
// Create a USB device handle so that we can communicate with the
// underlying USB stack. The WDFUSBDEVICE handle is used to query,
// configure, and manage all aspects of the USB device.
// These aspects include device properties, bus properties,
// and I/O creation and synchronization. We only create device the first
// the PrepareHardware is called. If the device is restarted by pnp manager
// for resource rebalance, we will use the same device handle but then select
// the interfaces again because the USB stack could reconfigure the device on
// restart.
//
if (pDeviceContext->UsbDevice == NULL) {
status = WdfUsbTargetDeviceCreate(Device,
WDF_NO_OBJECT_ATTRIBUTES,
&pDeviceContext->UsbDevice);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfUsbTargetDeviceCreate failed with Status code %!STATUS!\n", status);
return status;
}
}
status = SelectInterfaces(Device);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"SelectInterfaces failed 0x%x\n", status);
return status;
}
//
// Retrieve USBD version information, port driver capabilites and device
// capabilites such as speed, power, etc.
//
WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo);
status = WdfUsbTargetDeviceRetrieveInformation(
pDeviceContext->UsbDevice,
&deviceInfo);
if (NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "IsDeviceHighSpeed: %s\n",
(deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) ? "TRUE" : "FALSE");
}
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
"IsDeviceSelfPowered: %s\n",
(deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_SELF_POWERED) ? "TRUE" : "FALSE");
waitWakeEnable = deviceInfo.Traits &
WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
"IsDeviceRemoteWakeable: %s\n",
waitWakeEnable ? "TRUE" : "FALSE");
//
// Enable wait-wake and idle timeout if the device supports it
//
if(waitWakeEnable){
status = OsrFxSetPowerPolicy(Device);
if (!NT_SUCCESS (status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"OsrFxSetPowerPolicy failed %!STATUS!\n", status);
return status;
}
}
status = OsrFxConfigContReaderForInterruptEndPoint(pDeviceContext);
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- EvtDevicePrepareHardware\n");
return status;
}
NTSTATUS
OsrFxEvtDeviceD0Entry(
__in WDFDEVICE Device,
__in WDF_POWER_DEVICE_STATE PreviousState
)
/*++
Routine Description:
EvtDeviceD0Entry event callback must perform any operations that are
necessary before the specified device is used. It will be called every
time the hardware needs to be (re-)initialized.
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.
This function runs at PASSIVE_LEVEL, even though it is not paged. A
driver can optionally make this function pageable if DO_POWER_PAGABLE
is set. Even if DO_POWER_PAGABLE isn't set, this function still runs
at PASSIVE_LEVEL. In this case, though, the function absolutely must
not do anything that will cause a page fault.
Arguments:
Device - Handle to a framework device object.
PreviousState - Device power state which the device was in most recently.
If the device is being newly started, this will be
PowerDeviceUnspecified.
Return Value:
NTSTATUS
--*/
{
PDEVICE_CONTEXT pDeviceContext;
NTSTATUS status;
pDeviceContext = GetDeviceContext(Device);
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER,
"-->OsrFxEvtEvtDeviceD0Entry - coming from %s\n",
DbgDevicePowerString(PreviousState));
status = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe));
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtEvtDeviceD0Entry\n");
return status;
}
NTSTATUS
OsrFxEvtDeviceD0Exit(
__in WDFDEVICE Device,
__in WDF_POWER_DEVICE_STATE TargetState
)
/*++
Routine Description:
This routine undoes anything done in EvtDeviceD0Entry. 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.
EvtDeviceD0Exit event callback must perform any operations that are
necessary before the specified device is moved out of the D0 state. If the
driver needs to save hardware state before the device is powered down, then
that should be done here.
This function runs at PASSIVE_LEVEL, though it is generally not paged. A
driver can optionally make this function pageable if DO_POWER_PAGABLE is set.
Even if DO_POWER_PAGABLE isn't set, this function still runs at
PASSIVE_LEVEL. In this case, though, the function absolutely must not do
anything that will cause a page fault.
Arguments:
Device - Handle to a framework device object.
TargetState - Device power state which the device will be put in once this
callback is complete.
Return Value:
Success implies that the device can be used. Failure will result in the
device stack being torn down.
--*/
{
PDEVICE_CONTEXT pDeviceContext;
PAGED_CODE();
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER,
"-->OsrFxEvtDeviceD0Exit - moving to %s\n",
DbgDevicePowerString(TargetState));
pDeviceContext = GetDeviceContext(Device);
WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(pDeviceContext->InterruptPipe),
WdfIoTargetCancelSentIo);
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--OsrFxEvtDeviceD0Exit\n");
return STATUS_SUCCESS;
}
__drv_requiresIRQL(PASSIVE_LEVEL)
NTSTATUS
OsrFxSetPowerPolicy(
__in WDFDEVICE Device
)
{
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
//
// Init the idle policy structure.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleUsbSelectiveSuspend);
idleSettings.IdleTimeout = 10000; // 10-sec
status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
if ( !NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n", status);
return status;
}
//
// Init wait-wake policy structure.
//
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceAssignSxWakeSettings failed %x\n", status);
return status;
}
return status;
}
__drv_requiresIRQL(PASSIVE_LEVEL)
NTSTATUS
SelectInterfaces(
__in WDFDEVICE Device
)
/*++
Routine Description:
This helper routine selects the configuration, interface and
creates a context for every pipe (end point) in that interface.
Arguments:
Device - Handle to a framework device
Return Value:
NT status value
--*/
{
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams;
NTSTATUS status;
PDEVICE_CONTEXT pDeviceContext;
WDFUSBPIPE pipe;
WDF_USB_PIPE_INFORMATION pipeInfo;
UCHAR index;
UCHAR numberConfiguredPipes;
PAGED_CODE();
pDeviceContext = GetDeviceContext(Device);
WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE( &configParams);
status = WdfUsbTargetDeviceSelectConfig(pDeviceContext->UsbDevice,
WDF_NO_OBJECT_ATTRIBUTES,
&configParams);
if(!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfUsbTargetDeviceSelectConfig failed %!STATUS!\n",
status);
return status;
}
pDeviceContext->UsbInterface =
configParams.Types.SingleInterface.ConfiguredUsbInterface;
numberConfiguredPipes = configParams.Types.SingleInterface.NumberConfiguredPipes;
//
// Get pipe handles
//
for(index=0; index < numberConfiguredPipes; index++) {
WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
pipe = WdfUsbInterfaceGetConfiguredPipe(
pDeviceContext->UsbInterface,
index, //PipeIndex,
&pipeInfo
);
//
// Tell the framework that it's okay to read less than
// MaximumPacketSize
//
WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(pipe);
if(WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) {
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL,
"Interrupt Pipe is 0x%p\n", pipe);
pDeviceContext->InterruptPipe = pipe;
}
if(WdfUsbPipeTypeBulk == pipeInfo.PipeType &&
WdfUsbTargetPipeIsInEndpoint(pipe)) {
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL,
"BulkInput Pipe is 0x%p\n", pipe);
pDeviceContext->BulkReadPipe = pipe;
}
if(WdfUsbPipeTypeBulk == pipeInfo.PipeType &&
WdfUsbTargetPipeIsOutEndpoint(pipe)) {
TraceEvents(TRACE_LEVEL_INFORMATION, DBG_IOCTL,
"BulkOutput Pipe is 0x%p\n", pipe);
pDeviceContext->BulkWritePipe = pipe;
}
}
//
// If we didn't find all the 3 pipes, fail the start.
//
if(!(pDeviceContext->BulkWritePipe
&& pDeviceContext->BulkReadPipe && pDeviceContext->InterruptPipe)) {
status = STATUS_INVALID_DEVICE_STATE;
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"Device is not configured properly %!STATUS!\n",
status);
return status;
}
return status;
}
__drv_requiresIRQL(PASSIVE_LEVEL)
PCHAR
DbgDevicePowerString(
__in WDF_POWER_DEVICE_STATE Type
)
/*++
Updated Routine Description:
DbgDevicePowerString does not change in this stage of the function driver.
--*/
{
switch (Type)
{
case WdfPowerDeviceInvalid:
return "WdfPowerDeviceInvalid";
case WdfPowerDeviceD0:
return "WdfPowerDeviceD0";
case PowerDeviceD1:
return "WdfPowerDeviceD1";
case WdfPowerDeviceD2:
return "WdfPowerDeviceD2";
case WdfPowerDeviceD3:
return "WdfPowerDeviceD3";
case WdfPowerDeviceD3Final:
return "WdfPowerDeviceD3Final";
case WdfPowerDevicePrepareForHibernation:
return "WdfPowerDevicePrepareForHibernation";
case WdfPowerDeviceMaximum:
return "PowerDeviceMaximum";
default:
return "UnKnown Device Power State";
}
}
댓글 없음:
댓글 쓰기