WDF驱动学习:驱动对象,设备对象(二)

DeviceAdd例程:PnP管理器调用AddDevice时框架调用。它要 创建设备对象,一个或多个IO队列和设备GUID接口等。驱动此时注册各种即插即用、电源管理例程以及各种回调函数。驱动此时可以查询任何接口或者获取总线驱动的配置空间信息,但是不能访问硬件的寄存器以及初始化设备。

PCI

NTSTATUS
PLxEvtDeviceAdd(
    IN WDFDRIVER        Driver,
    IN PWDFDEVICE_INIT  DeviceInit
    )
/*++

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                   status = STATUS_SUCCESS;
    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES       attributes;
    WDFDEVICE                   device;
    PDEVICE_EXTENSION           devExt = NULL;

    UNREFERENCED_PARAMETER( Driver );

   // TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,  "--> PLxEvtDeviceAdd");

    PAGED_CODE();

    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 = PLxEvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = PLxEvtDeviceReleaseHardware;

    //
    // 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.EvtDeviceD0Entry         = PLxEvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit          = PLxEvtDeviceD0Exit;

    //
    // 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 = WdfSynchronizationScopeDevice;

    //
    // Create the device
    //
    status = WdfDeviceCreate( &DeviceInit, &attributes, &device );

    if (!NT_SUCCESS(status)) {
        //
        // Device Initialization failed.
        //
       /* TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "DeviceCreate failed %!STATUS!", status);*/
        return status;
    }

    //
    // Get the DeviceExtension and initialize it. PLxGetDeviceContext is an inline function
    // defined by WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in the
    // private header file. This function will do the type checking and return
    // the device context. If you pass a wrong object a wrong object handle
    // it will return NULL and assert if run under framework verifier mode.
    //
    devExt = PLxGetDeviceContext(device);

    devExt->Device = device;

   /* TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "     AddDevice PDO (0x%p) FDO (0x%p), DevExt (0x%p)",
                WdfDeviceWdmGetPhysicalDevice(device),
                WdfDeviceWdmGetDeviceObject(device), devExt);*/

    //
    // Tell the Framework that this device will need an interface
    //
    // NOTE: See the note in Public.h concerning this GUID value.
    //
    status = WdfDeviceCreateDeviceInterface( device,
                                             (LPGUID) &GUID_PLX_INTERFACE,
                                             NULL );

    if (!NT_SUCCESS(status)) {
       /* TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "<-- DeviceCreateDeviceInterface "
                    "failed %!STATUS!", status);*/
        return status;
    }

    //
    // Set the idle and wait-wake policy for this device.
    //
    status = PLxSetIdleAndWakeSettings(devExt);

    if (!NT_SUCCESS (status)) {
        //
        // NOTE: The attempt to set the Idle and Wake options
        //       is a best-effort try. Failure is probably due to
        //       the non-driver environmentals, such as the system,
        //       bus or OS indicating that Wake is not supported for
        //       this case.
        //       All that being said, it probably not desirable to
        //       return the failure code as it would cause the
        //       AddDevice to fail and Idle and Wake are probably not
        //       "must-have" options.
        //
        //       You must decide for your case whether Idle/Wake are
        //       "must-have" options...but my guess is probably not.
        //
#if 1
        status = STATUS_SUCCESS;
#else
        return status;
#endif
    }

    //
    // Initalize the Device Extension.
    //
    status = PLxInitializeDeviceExtension(devExt);

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

    /*TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "<-- PLxEvtDeviceAdd %!STATUS!", status);
*/
    return status;
}

USB

NTSTATUS
UsbSamp_EvtDeviceAdd(
    WDFDRIVER        Driver,
    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_FILEOBJECT_CONFIG     fileConfig;
    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;
    ULONG                               maximumTransferSize;

    UNREFERENCED_PARAMETER(Driver);

    UsbSamp_DbgPrint (3, ("UsbSamp_EvtDeviceAdd routine\n"));

    PAGED_CODE();

    //
    // 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);

    pnpPowerCallbacks.EvtDevicePrepareHardware = UsbSamp_EvtDevicePrepareHardware;

    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    //
    // Initialize the request attributes to specify the context size and type
    // for every request created by framework for this device.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT);

    WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes);

    //
    // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the
    // framework whether you are interested in handle Create, Close and
    // Cleanup requests that gets genereate when an application or another
    // kernel component opens an handle to the device. If you don't register
    // the framework default behaviour would be complete these requests
    // with STATUS_SUCCESS. A driver might be interested in registering these
    // events if it wants to do security validation and also wants to maintain
    // per handle (fileobject) context.
    //

    WDF_FILEOBJECT_CONFIG_INIT(
        &fileConfig,
        UsbSamp_EvtDeviceFileCreate,
        WDF_NO_EVENT_CALLBACK,
        WDF_NO_EVENT_CALLBACK
        );

    //
    // Specify a context for FileObject. If you register FILE_EVENT callbacks,
    // the framework by default creates a framework FILEOBJECT corresponding
    // to the WDM fileobject. If you want to track any per handle context,
    // use the context for FileObject. Driver that typically use FsContext
    // field should instead use Framework FileObject context.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FILE_CONTEXT);

    WdfDeviceInitSetFileObjectConfig(DeviceInit,
                                       &fileConfig,
                                       &attributes);

#if !defined(BUFFERED_READ_WRITE)
    //
    // I/O type is Buffered by default. We want to do direct I/O for Reads
    // and Writes so set it explicitly. Please note that this sample
    // can do isoch transfer only if the io type is directio.
    //
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);

#endif

    //
    // 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);
    attributes.EvtCleanupCallback = UsbSamp_EvtDeviceContextCleanup;

    status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfDeviceCreate failed with Status code 0x%x\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);

    //
    //Get MaximumTransferSize from registry
    //
    maximumTransferSize = 0;

    ReadFdoRegistryKeyValue(Driver,
                              L"MaximumTransferSize",
                              &maximumTransferSize);

    if (maximumTransferSize){
        pDevContext->MaximumTransferSize = maximumTransferSize;
    }
    else {
        pDevContext->MaximumTransferSize = DEFAULT_REGISTRY_TRANSFER_SIZE;
    }

    //
    // 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);

    //
    // Register I/O callbacks to tell the framework that you are interested
    // in handling WdfRequestTypeRead, WdfRequestTypeWrite, and 
    // IRP_MJ_DEVICE_CONTROL requests.
    // WdfIoQueueDispatchParallel means that we are capable of handling
    // all the I/O request simultaneously and we are responsible for protecting
    // data that could be accessed by these callbacks simultaneously.
    // This queue will be,  by default,  automanaged by the framework with
    // respect to PNP and Power events. That is, framework will take care
    // of queuing, failing, dispatching incoming requests based on the current
    // pnp/power state of the device.
    //

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
                                           WdfIoQueueDispatchParallel);

    ioQueueConfig.EvtIoRead = UsbSamp_EvtIoRead;
    ioQueueConfig.EvtIoWrite = UsbSamp_EvtIoWrite;
    ioQueueConfig.EvtIoDeviceControl = UsbSamp_EvtIoDeviceControl;
    ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;

    status = WdfIoQueueCreate(device,
                              &ioQueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &queue);// pointer to default queue
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for Default Queue 0x%x\n", status));
        return status;
    }

    //
    // Create a synchronized manual queue so we can retrieve one read request at a
    // time and dispatch it to the lower driver with the right StartFrame number.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                              WdfIoQueueDispatchManual);
    
     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
     attributes.SynchronizationScope=WdfSynchronizationScopeQueue;
     
     ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;
    
     status = WdfIoQueueCreate(device,
                               &ioQueueConfig,
                               &attributes,
                               &pDevContext->IsochReadQueue);
     if (!NT_SUCCESS(status)) {
         UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for isoch 0x%x\n", status));
         return status;
     }

    //
    // Register a ready notification routine so we get notified whenever the queue transitions
    // from empty to non-empty state.
    //
    status = WdfIoQueueReadyNotify(pDevContext->IsochReadQueue,
                                   UsbSamp_EvtIoQueueReadyNotification,
                                   (WDFCONTEXT)pDevContext);
    
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed  for isoch 0x%x\n", status));
        return status;
    }

    //
    // Create a synchronized manual queue so we can retrieve one write request at a
    // time and dispatch it to the lower driver with the right StartFrame number.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                              WdfIoQueueDispatchManual);
    
     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
     attributes.SynchronizationScope=WdfSynchronizationScopeQueue;
     
     ioQueueConfig.EvtIoStop = UsbSamp_EvtIoStop;
    
     status = WdfIoQueueCreate(device,
                               &ioQueueConfig,
                               &attributes,
                               &pDevContext->IsochWriteQueue);
     if (!NT_SUCCESS(status)) {
         UsbSamp_DbgPrint(1, ("WdfIoQueueCreate failed  for isoch 0x%x\n", status));
         return status;
     }

    //
    // Register a ready notification routine so we get notified whenever the queue transitions
    // from empty to non-empty state.
    //
    status = WdfIoQueueReadyNotify(pDevContext->IsochWriteQueue,
                                   UsbSamp_EvtIoQueueReadyNotification,
                                   (WDFCONTEXT)pDevContext);
    
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfIoQueueReadyNotify failed  for isoch 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_CLASS_USBSAMP_USB,
                        NULL);// Reference String
    if (!NT_SUCCESS(status)) {
        UsbSamp_DbgPrint(1, ("WdfDeviceCreateDeviceInterface failed  0x%x\n", status));
        return status;
    }

    status = USBD_CreateHandle(WdfDeviceWdmGetDeviceObject(device),    
                               WdfDeviceWdmGetAttachedDevice(device),   
                               USBD_CLIENT_CONTRACT_VERSION_602,   
                               POOL_TAG,   
                               &pDevContext->UsbdHandle);   
    if(!NT_SUCCESS(status)){
        UsbSamp_DbgPrint(1, ("USBD_CreateHandle failed 0x%x", status));
        return status;
    }

    UsbSamp_DbgPrint(3, ("EvtDriverDeviceAdd - ends\n"));

    return status;
}

串口

NTSTATUS
SerialEvtDeviceAdd(
    IN WDFDRIVER Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
/*++

Routine Description:

    EvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager.


Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/

{
    NTSTATUS                      status;
    PSERIAL_DEVICE_EXTENSION      pDevExt;
    static ULONG                  currentInstance = 0;
    WDF_FILEOBJECT_CONFIG         fileobjectConfig;
    WDFDEVICE                     device;
    WDF_PNPPOWER_EVENT_CALLBACKS  pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES         attributes;
    WDF_IO_QUEUE_CONFIG           queueConfig;
    WDFQUEUE                      defaultqueue;
    ULONG                         isMulti;
    PULONG                        countSoFar;
    WDF_INTERRUPT_CONFIG          interruptConfig;
    PSERIAL_INTERRUPT_CONTEXT     interruptContext;
    ULONG                         relinquishPowerPolicy;

    DECLARE_UNICODE_STRING_SIZE(deviceName, DEVICE_OBJECT_NAME_LENGTH);

    PAGED_CODE();

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "-->SerialEvtDeviceAdd\n");

    status = RtlUnicodeStringPrintf(&deviceName, L"%ws%u",
                                L"\\Device\\Serial",
                                currentInstance++);


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

    status = WdfDeviceInitAssignName(DeviceInit,& deviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    WdfDeviceInitSetExclusive(DeviceInit, TRUE);
    WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SERIAL_PORT);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT);

    WdfDeviceInitSetRequestAttributes(DeviceInit, &attributes);

    //
    // 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.  These next two callbacks set up and tear down hardware state,
    // specifically that which only has to be done once.
    //

    pnpPowerCallbacks.EvtDevicePrepareHardware = SerialEvtPrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = SerialEvtReleaseHardware;

    //
    // 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.EvtDeviceD0Entry         = SerialEvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit          = SerialEvtDeviceD0Exit;

    //
    // Specify the callback for monitoring when the device's interrupt are
    // enabled or about to be disabled.
    //

    pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = SerialEvtDeviceD0EntryPostInterruptsEnabled;
    pnpPowerCallbacks.EvtDeviceD0ExitPreInterruptsDisabled  = SerialEvtDeviceD0ExitPreInterruptsDisabled;

    //
    // Register the PnP and power callbacks.
    //
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    if ( !NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                         "WdfDeviceInitSetPnpPowerEventCallbacks failed %!STATUS!\n",
                         status);
        return status;
    }

    //
    // Find out if we own power policy
    //
    SerialGetFdoRegistryKeyValue( DeviceInit,
                                  L"SerialRelinquishPowerPolicy",
                                  &relinquishPowerPolicy );

    if(relinquishPowerPolicy) {
        //
        // FDO's are assumed to be power policy owner by default. So tell
        // the framework explicitly to relinquish the power policy ownership.
        //
        SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
                         "RelinquishPowerPolicy due to registry settings\n");

        WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
    }

    //
    // For Windows XP and below, we will register for the WDM Preprocess callback
    // for IRP_MJ_CREATE. This is done because, the Serenum filter doesn't handle
    // creates that are marked pending. Since framework always marks the IRP pending,
    // we are registering this WDM preprocess handler so that we can bypass the
    // framework and handle the create and close ourself. This workaround is need
    // only if you intend to install the Serenum as an upper filter.
    //
    if (RtlIsNtDdiVersionAvailable(NTDDI_VISTA) == FALSE) {

        status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
                                                DeviceInit,
                                                SerialWdmDeviceFileCreate,
                                                IRP_MJ_CREATE,
                                                NULL, // pointer minor function table
                                                0); // number of entries in the table

        if (!NT_SUCCESS(status)) {
            SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                             "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
                             status);
            return status;
        }

        status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
                                                DeviceInit,
                                                SerialWdmFileClose,
                                                IRP_MJ_CLOSE,
                                                NULL, // pointer minor function table
                                                0); // number of entries in the table

        if (!NT_SUCCESS(status)) {
            SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                             "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
                             status);
            return status;
        }

    } else {

        //
        // FileEvents can opt for Device level synchronization only if the ExecutionLevel
        // of the Device is passive. Since we can't choose passive execution-level for
        // device because we have chose to synchronize timers & dpcs with the device,
        // we will opt out of synchonization with the device for fileobjects.
        // Note: If the driver has to synchronize Create with the other I/O events,
        // it can create a queue and configure-dispatch create requests to the queue.
        //
        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        attributes.SynchronizationScope = WdfSynchronizationScopeNone;

        //
        // Set Entry points for Create and Close..
        //
        WDF_FILEOBJECT_CONFIG_INIT(
                            &fileobjectConfig,
                            SerialEvtDeviceFileCreate,
                            SerialEvtFileClose,
                            WDF_NO_EVENT_CALLBACK // Cleanup
                            );

        WdfDeviceInitSetFileObjectConfig(
                        DeviceInit,
                        &fileobjectConfig,
                        &attributes
                        );
    }


    //
    // Since framework queues doesn't handle IRP_MJ_FLUSH_BUFFERS,
    // IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION requests,
    // we will register a preprocess callback to handle them.
    //
    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
                                            DeviceInit,
                                            SerialFlush,
                                            IRP_MJ_FLUSH_BUFFERS,
                                            NULL, // pointer minor function table
                                            0); // number of entries in the table

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                         "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
                         status);
        return status;
    }

    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
                                        DeviceInit,
                                        SerialQueryInformationFile,
                                        IRP_MJ_QUERY_INFORMATION,
                                        NULL, // pointer minor function table
                                        0); // number of entries in the table

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                         "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
                         status);
        return status;
    }
    status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
                                        DeviceInit,
                                        SerialSetInformationFile,
                                        IRP_MJ_SET_INFORMATION,
                                        NULL, // pointer minor function table
                                        0); // number of entries in the table

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                         "WdfDeviceInitAssignWdmIrpPreprocessCallback failed %!STATUS!\n",
                         status);
        return status;
    }


    //
    // Create a device
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE (&attributes,
                                            SERIAL_DEVICE_EXTENSION);
    //
    // Provide a callback to cleanup the context. This will be called
    // when the device is removed.
    //
    attributes.EvtCleanupCallback = SerialEvtDeviceContextCleanup;
    //
    // 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, dpcs,
    // and timers. By doing that we don't have to worrry about synchronizing
    // access to device-context by Io Events, cancel-routine, timer and dpc
    // callbacks.
    //
    attributes.SynchronizationScope = WdfSynchronizationScopeDevice;

    status = WdfDeviceCreate(&DeviceInit, &attributes, &device);
    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                       "SerialAddDevice - WdfDeviceCreate failed %!STATUS!\n",
                         status);
        return status;
    }

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
                     "Created device (%p) %wZ\n", device, &deviceName);

    pDevExt = SerialGetDeviceExtension (device);

    pDevExt->DriverObject = WdfDriverWdmGetDriverObject(Driver);

    //
    // This sample doesn't support multiport serial devices.
    // Multiport devices allow other pseudo-serial devices with extra
    // resources to specify another range of I/O ports.
    //
    if(!SerialGetRegistryKeyValue(device, L"MultiportDevice",  &isMulti)) {
        isMulti = 0;
    }

    if(isMulti) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,
                         "This sample doesn't support multiport devices\n");
        return STATUS_DEVICE_CONFIGURATION_ERROR;
    }

    //
    // Set up the device extension.
    //

    pDevExt = SerialGetDeviceExtension (device);

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP,
                     "AddDevice PDO(0x%p) FDO(0x%p), Lower(0x%p) DevExt (0x%p)\n",
                    WdfDeviceWdmGetPhysicalDevice (device),
                    WdfDeviceWdmGetDeviceObject (device),
                    WdfDeviceWdmGetAttachedDevice(device),
                    pDevExt);

    pDevExt->DeviceIsOpened = FALSE;
    pDevExt->DeviceObject   = WdfDeviceWdmGetDeviceObject(device);
    pDevExt->WdfDevice = device;

    pDevExt->TxFifoAmount           = driverDefaults.TxFIFODefault;
    pDevExt->UartRemovalDetect      = driverDefaults.UartRemovalDetect;
    pDevExt->CreatedSymbolicLink    = FALSE;
    pDevExt->OwnsPowerPolicy = relinquishPowerPolicy ? FALSE : TRUE;

    status = SerialSetPowerPolicy(pDevExt);
    if(!NT_SUCCESS(status)){
        return status;
    }

    //
    // We create four manual queues below.
    // Read Queue..(how about using serial queue for read). Since requests
    // jump from queue to queue, we cannot configure the queues to receive a
    // particular type of request. For example, some of the IOCTLs end up
    // in read and write queue.
    //
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
                             WdfIoQueueDispatchManual);

    queueConfig.EvtIoStop = SerialEvtIoStop;
    queueConfig.EvtIoResume = SerialEvtIoResume;
    queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;

    status = WdfIoQueueCreate (device,
                               &queueConfig,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &pDevExt->ReadQueue
                               );

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, " WdfIoQueueCreate for Read failed %!STATUS!\n", status);
        return status;
    }

    //
    // Write Queue..
    //
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
                             WdfIoQueueDispatchManual);

    queueConfig.EvtIoStop = SerialEvtIoStop;
    queueConfig.EvtIoResume = SerialEvtIoResume;
    queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;

    status = WdfIoQueueCreate (device,
                               &queueConfig,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &pDevExt->WriteQueue
                               );

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  " WdfIoQueueCreate for Write failed %!STATUS!\n", status);
        return status;
    }

    //
    // Mask Queue...
    //
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
                             WdfIoQueueDispatchManual
                             );

    queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;

    queueConfig.EvtIoStop = SerialEvtIoStop;
    queueConfig.EvtIoResume = SerialEvtIoResume;

    status = WdfIoQueueCreate (device,
                               &queueConfig,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &pDevExt->MaskQueue
                               );

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  " WdfIoQueueCreate for Mask failed %!STATUS!\n",   status);
        return status;
    }

    //
    // Purge Queue..
    //
    WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
                             WdfIoQueueDispatchManual
                             );

    queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;

    queueConfig.EvtIoStop = SerialEvtIoStop;
    queueConfig.EvtIoResume = SerialEvtIoResume;

    status = WdfIoQueueCreate (device,
                               &queueConfig,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &pDevExt->PurgeQueue
                               );

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  " WdfIoQueueCreate for Purge failed %!STATUS!\n",   status);
        return status;
    }

    //
    // All the incoming I/O requests are routed to the default queue and dispatch to the
    // appropriate callback events. These callback event will check to see if another
    // request is currently active. If so then it will forward it to other manual queues.
    // All the queues are auto managed by the framework in response to the PNP
    // and Power events.
    //
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
                &queueConfig,
                WdfIoQueueDispatchParallel
                );
    queueConfig.EvtIoRead   = SerialEvtIoRead;
    queueConfig.EvtIoWrite  = SerialEvtIoWrite;
    queueConfig.EvtIoDeviceControl = SerialEvtIoDeviceControl;
    queueConfig.EvtIoInternalDeviceControl = SerialEvtIoInternalDeviceControl;
    queueConfig.EvtIoCanceledOnQueue = SerialEvtCanceledOnQueue;

    queueConfig.EvtIoStop = SerialEvtIoStop;
    queueConfig.EvtIoResume = SerialEvtIoResume;

    status = WdfIoQueueCreate(device,
                                         &queueConfig,
                                         WDF_NO_OBJECT_ATTRIBUTES,
                                         &defaultqueue
                                         );
    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed %!STATUS!\n", status);
        return status;
    }

    //
    // Create WDFINTERRUPT object. Let us leave the ShareVector to  default value and
    // let the framework decide whether to share the interrupt or not based on the
    // ShareDisposition provided by the bus driver in the resource descriptor.
    //

    WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
                              SerialISR,
                              NULL);

    interruptConfig.EvtInterruptDisable = SerialEvtInterruptDisable;
    interruptConfig.EvtInterruptEnable = SerialEvtInterruptEnable;

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, SERIAL_INTERRUPT_CONTEXT);

    status = WdfInterruptCreate(device,
                                &interruptConfig,
                                &attributes,
                                &pDevExt->WdfInterrupt);

    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "Couldn't create interrupt for %wZ\n",
                  &pDevExt->DeviceName);
        return status;
    }

    //
    // Interrupt state wait lock...
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.ParentObject = pDevExt->WdfInterrupt;

    interruptContext = SerialGetInterruptContext(pDevExt->WdfInterrupt);

    status = WdfWaitLockCreate(&attributes,
                               &interruptContext->InterruptStateLock
                               );

    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP,  " WdfWaitLockCreate for InterruptStateLock failed %!STATUS!\n",   status);
        return status;
    }

    //
    // Set interrupt policy
    //
    SerialSetInterruptPolicy(pDevExt->WdfInterrupt);

    //
    // Timers and DPCs...
    //
    status = SerialCreateTimersAndDpcs(pDevExt);
    if (!NT_SUCCESS(status)) {

        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialCreateTimersAndDpcs failed %x\n", status);
        return status;
    }

    //
    // Register with WMI.
    //
    status = SerialWmiRegistration(device);
    if(!NT_SUCCESS (status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "SerialWmiRegistration failed %!STATUS!\n", status);
        return status;

    }

    //
    // Upto this point, if we fail, we don't have to worry about freeing any resource because
    // framework will free all the objects.
    //
    //
    // Do the external naming.
    //

    status = SerialDoExternalNaming(pDevExt);
    if (!NT_SUCCESS(status)) {
        SerialDbgPrintEx(TRACE_LEVEL_ERROR, DBG_PNP, "External Naming Failed - Status %!STATUS!\n",
                      status);
        return status;
    }

    //
    // Finally increment the global system configuration that keeps track of number of serial ports.
    //
    countSoFar = &IoGetConfigurationInformation()->SerialCount;
    (*countSoFar)++;
    pDevExt->IsSystemConfigInfoUpdated = TRUE;

    SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_PNP, "<--SerialEvtDeviceAdd\n");

    return status;

}


你可能感兴趣的:(WDF驱动学习:驱动对象,设备对象(二))