// // WMI System Call back functions // NTSTATUS Bus_EvtStdDataSetItem( IN WDFWMIINSTANCE WmiInstance, IN ULONG DataItemId, IN ULONG InBufferSize, IN PVOID InBuffer ) /*++ Routine Description: This routine is a callback into the driver to set for the contents of an instance. Arguments: WmiInstance is the instance being set DataItemId has the id of the data item being set InBufferSize has the size of the data item passed InBuffer has the new values for the data item Return Value: status --*/ { PFDO_DEVICE_DATA fdoData; PAGED_CODE(); fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); // // TODO: Use generated header's #defines for constants and sizes // (for the remainder of the file) // if (DataItemId == 2) { if (InBufferSize < sizeof(ULONG)) { return STATUS_BUFFER_TOO_SMALL; } BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = *((PULONG)InBuffer); return STATUS_SUCCESS; } // // All other fields are read only // return STATUS_WMI_READ_ONLY; }
BOOLEAN NICEvtInterruptIsr( IN WDFINTERRUPT Interrupt, IN ULONG MessageID ) /*++ Routine Description: Interrupt handler for the device. Arguments: Interupt - Address of the framework interrupt object MessageID - Return Value: TRUE if our device is interrupting, FALSE otherwise. --*/ { BOOLEAN InterruptRecognized = FALSE; PFDO_DATA FdoData = NULL; USHORT IntStatus; UNREFERENCED_PARAMETER( MessageID ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "--> NICEvtInterruptIsr\n"); FdoData = FdoGetData(WdfInterruptGetDevice(Interrupt)); // // We process the interrupt if it's not disabled and it's active // if (!NIC_INTERRUPT_DISABLED(FdoData) && NIC_INTERRUPT_ACTIVE(FdoData)) { InterruptRecognized = TRUE; // // Disable the interrupt (will be re-enabled in NICEvtInterruptDpc // NICDisableInterrupt(FdoData); // // Acknowledge the interrupt(s) and get the interrupt status // NIC_ACK_INTERRUPT(FdoData, IntStatus); WdfInterruptQueueDpcForIsr( Interrupt ); } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INTERRUPT, "<-- NICEvtInterruptIsr\n"); return InterruptRecognized; }
NTSTATUS NICEvtDeviceD0EntryPostInterruptsEnabled( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) /*++ Routine Description: This event is called so that driver can do PASSIVE_LEVEL work after the interrupt is connected and enabled. Here we start the watchdog timer. Watch dog timer is used to do the initial link detection during start and then used to make sure the device is not stuck for any reason. 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: Interrupt - Handle to a Framework interrupt object. AssociatedDevice - Handle to a Framework device object. Return Value: STATUS_SUCCESS - indicates success. --*/ { PFDO_DATA fdoData; UNREFERENCED_PARAMETER( PreviousState ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0EntryPostInterruptsEnabled\n"); fdoData = FdoGetData(Device); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0EntryPostInterruptsEnabled\n"); return STATUS_SUCCESS; }
NTSTATUS Bus_EvtStdDataQueryInstance( IN WDFWMIINSTANCE WmiInstance, IN ULONG OutBufferSize, IN PVOID OutBuffer, OUT PULONG BufferUsed ) /*++ Routine Description: This routine is a callback into the driver to set for the contents of a wmi instance Arguments: WmiInstance is the instance being set OutBufferSize on has the maximum size available to write the data block. OutBuffer on return is filled with the returned data block BufferUsed pointer containing how many bytes are required (upon failure) or how many bytes were used (upon success) Return Value: status --*/ { PFDO_DEVICE_DATA fdoData; UNREFERENCED_PARAMETER(OutBufferSize); PAGED_CODE(); fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); *BufferUsed = sizeof (TOASTER_BUS_WMI_STD_DATA); * (PTOASTER_BUS_WMI_STD_DATA) OutBuffer = fdoData->StdToasterBusData; return STATUS_SUCCESS; }
NTSTATUS Bus_EvtStdDataSetInstance( IN WDFWMIINSTANCE WmiInstance, IN ULONG InBufferSize, IN PVOID InBuffer ) /*++ Routine Description: This routine is a callback into the driver to set for the contents of an instance. Arguments: WmiInstance is the instance being set BufferSize has the size of the data block passed Buffer has the new values for the data block Return Value: status --*/ { PFDO_DEVICE_DATA fdoData; UNREFERENCED_PARAMETER(InBufferSize); PAGED_CODE(); fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); // // We will update only writable elements. // BusEnumDebugLevel = fdoData->StdToasterBusData.DebugPrintLevel = ((PTOASTER_BUS_WMI_STD_DATA)InBuffer)->DebugPrintLevel; return STATUS_SUCCESS; }
NTSTATUS NICEvtDeviceD0ExitPreInterruptsDisabled( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState ) /*++ Routine Description: This event is called so that driver can do PASSIVE_LEVEL work before the interrupt is disconnected and disabled. Arguments: Interrupt - Handle to a Framework interrupt object. AssociatedDevice - Handle to a Framework device object. Return Value: STATUS_SUCCESS - indicates success. --*/ { PFDO_DATA fdoData; UNREFERENCED_PARAMETER(TargetState); PAGED_CODE(); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtDeviceD0ExitPreInterruptsDisabled\n"); fdoData = FdoGetData(Device); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtDeviceD0ExitPreInterruptsDisabled\n"); return STATUS_SUCCESS; }
NTSTATUS NICEvtInterruptDisable( IN WDFINTERRUPT Interrupt, IN WDFDEVICE AssociatedDevice ) /*++ Routine Description: This event is called before the Framework moves the device to D1, D2 or D3 and before EvtDeviceD0Exit. The driver should disable its interrupt here. This function will be called at the device's assigned interrupt IRQL (DIRQL.) Arguments: Interrupt - Handle to a Framework interrupt object. AssociatedDevice - Handle to a Framework device object. Return Value: STATUS_SUCCESS - indicates success. --*/ { PFDO_DATA fdoData; UNREFERENCED_PARAMETER(Interrupt); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptDisable\n"); fdoData = FdoGetData(AssociatedDevice); NICDisableInterrupt(fdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptDisable\n"); return STATUS_SUCCESS; }
NTSTATUS NICEvtInterruptEnable( IN WDFINTERRUPT Interrupt, IN WDFDEVICE AssociatedDevice ) /*++ Routine Description: This event is called when the Framework moves the device to D0, and after EvtDeviceD0Entry. The driver should enable its interrupt here. This function will be called at the device's assigned interrupt IRQL (DIRQL.) Arguments: Interrupt - Handle to a Framework interrupt object. AssociatedDevice - Handle to a Framework device object. Return Value: BOOLEAN - TRUE indicates that the interrupt was successfully enabled. --*/ { PFDO_DATA fdoData; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "--> NICEvtInterruptEnable\n"); fdoData = FdoGetData(AssociatedDevice); NICEnableInterrupt(Interrupt, fdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_PNP, "<-- NICEvtInterruptEnable\n"); return STATUS_SUCCESS; }
VOID PciDrvEvtIoWrite( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) /*++ Routine Description: Called by the framework as soon as it receive a write IRP. If the device is not ready, fail the request. Otherwise get scatter-gather list for this request and send the packet to the hardware for DMA. Arguments: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Handle to a framework request object. Length - Length of the IO operation The default property of the queue is to not dispatch zero lenght read & write requests to the driver and complete is with status success. So we will never get a zero length request. Return Value: --*/ { NTSTATUS status; PFDO_DATA FdoData; WDFDEVICE hDevice; PMDL mdl = NULL; UNREFERENCED_PARAMETER(Length); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> PciDrvEvtIoWrite Request %p\n", Request); hDevice = WdfIoQueueGetDevice(Queue); FdoData = FdoGetData(hDevice); status = WdfRequestRetrieveInputWdmMdl(Request, &mdl); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "WdfRequestRetrieveInputWdmMdl failed %x\n", status); WdfRequestCompleteWithInformation(Request, status, 0); } else { status = NICInitiateDmaTransfer(FdoData, Request); if(!NT_SUCCESS(status)) { WdfRequestCompleteWithInformation(Request, status, 0); } } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- PciDrvEvtIoWrite %X\n", status); return; }
BOOLEAN NICEvtProgramDmaFunction( IN WDFDMATRANSACTION Transaction, IN WDFDEVICE Device, IN PVOID Context, IN WDF_DMA_DIRECTION Direction, IN PSCATTER_GATHER_LIST ScatterGather ) /*++ Routine Description: Arguments: Return Value: --*/ { PFDO_DATA fdoData; WDFREQUEST request; NTSTATUS status; UNREFERENCED_PARAMETER( Context ); UNREFERENCED_PARAMETER( Direction ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "--> NICEvtProgramDmaFunction\n"); fdoData = FdoGetData(Device); request = WdfDmaTransactionGetRequest(Transaction); WdfSpinLockAcquire(fdoData->SendLock); // // If tcb or link is not available, queue the request // if (!MP_TCB_RESOURCES_AVAIABLE(fdoData)) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "Resource is not available: queue Request %p\n", request); // // Must abort the transaction before deleting. // (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); ASSERT(NT_SUCCESS(status)); WdfObjectDelete( Transaction ); // // Queue the request for later processing. // status = WdfRequestForwardToIoQueue(request, fdoData->PendingWriteQueue); if(!NT_SUCCESS(status)) { ASSERTMSG(" WdfRequestForwardToIoQueue failed ", FALSE); WdfSpinLockRelease(fdoData->SendLock); WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); return FALSE; } fdoData->nWaitSend++; } else { status = NICWritePacket(fdoData, Transaction, ScatterGather); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_WRITE, "<-- NICEvtProgramDmaFunction returning %!STATUS!\n", status); // // Must abort the transaction before deleting. // (VOID )WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status); ASSERT(NT_SUCCESS(status)); WdfObjectDelete( Transaction ); WdfSpinLockRelease(fdoData->SendLock); WdfRequestCompleteWithInformation(request, STATUS_UNSUCCESSFUL, 0); return FALSE; } } WdfSpinLockRelease(fdoData->SendLock); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_WRITE, "<-- NICEvtProgramDmaFunction\n"); return TRUE; }
OutBuffer on return is filled with the returned data block BufferUsed pointer containing how many bytes are required (upon failure) or how many bytes were used (upon success) Return Value: status --*/ { PFDO_DEVICE_DATA fdoData; UNREFERENCED_PARAMETER(OutBufferSize); PAGED_CODE(); fdoData = FdoGetData(WdfWmiInstanceGetDevice(WmiInstance)); *BufferUsed = sizeof (fdoData->StdToasterBusData); if (OutBufferSize < sizeof(fdoData->StdToasterBusData)) { return STATUS_BUFFER_TOO_SMALL; } * (PTOASTER_BUS_WMI_STD_DATA) OutBuffer = fdoData->StdToasterBusData; return STATUS_SUCCESS; }
VOID NICEvtInterruptDpc( IN WDFINTERRUPT WdfInterrupt, IN WDFOBJECT WdfDevice ) /*++ Routine Description: DPC callback for ISR. Arguments: WdfInterrupt - Handle to the framework interrupt object WdfDevice - Associated device object. Return Value: --*/ { PFDO_DATA fdoData = NULL; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "--> NICEvtInterruptDpc\n"); fdoData = FdoGetData(WdfDevice); WdfSpinLockAcquire(fdoData->RcvLock); NICHandleRecvInterrupt(fdoData); WdfSpinLockRelease(fdoData->RcvLock); // // Handle send interrupt // WdfSpinLockAcquire(fdoData->SendLock); NICHandleSendInterrupt(fdoData); WdfSpinLockRelease(fdoData->SendLock); // // Check if any queued Sends need to be reprocessed. // NICCheckForQueuedSends(fdoData); // // Start the receive unit if it had stopped // WdfSpinLockAcquire(fdoData->RcvLock); NICStartRecv(fdoData); WdfSpinLockRelease(fdoData->RcvLock); // // Re-enable the interrupt (disabled in MPIsr) // WdfInterruptSynchronize( WdfInterrupt, NICEnableInterrupt, fdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_DPC, "<-- NICEvtInterruptDpc\n"); }
NTSTATUS Bus_PlugInDevice( _In_ WDFDEVICE Device, _In_ PWCHAR HardwareIds, _In_ ULONG SerialNo ) /*++ Routine Description: The user application has told us that a new device on the bus has arrived. We therefore need to create a new PDO, initialize it, add it to the list of PDOs for this FDO bus, and then tell Plug and Play that all of this happened so that it will start sending prodding IRPs. --*/ { NTSTATUS status = STATUS_SUCCESS; BOOLEAN unique = TRUE; WDFDEVICE hChild; PPDO_DEVICE_DATA pdoData; PFDO_DEVICE_DATA deviceData; PAGED_CODE (); // // First make sure that we don't already have another device with the // same serial number. // Framework creates a collection of all the child devices we have // created so far. So acquire the handle to the collection and lock // it before walking the item. // deviceData = FdoGetData(Device); hChild = NULL; // // We need an additional lock to synchronize addition because // WdfFdoLockStaticChildListForIteration locks against anyone immediately // updating the static child list (the changes are put on a queue until the // list has been unlocked). This type of lock does not enforce our concept // of unique IDs on the bus (ie SerialNo). // // Without our additional lock, 2 threads could execute this function, both // find that the requested SerialNo is not in the list and attempt to add // it. If that were to occur, 2 PDOs would have the same unique SerialNo, // which is incorrect. // // We must use a passive level lock because you can only call WdfDeviceCreate // at PASSIVE_LEVEL. // WdfWaitLockAcquire(deviceData->ChildLock, NULL); WdfFdoLockStaticChildListForIteration(Device); while ((hChild = WdfFdoRetrieveNextStaticChild(Device, hChild, WdfRetrieveAddedChildren)) != NULL) { // // WdfFdoRetrieveNextStaticChild returns reported and to be reported // children (ie children who have been added but not yet reported to PNP). // // A surprise removed child will not be returned in this list. // pdoData = PdoGetData(hChild); // // It's okay to plug in another device with the same serial number // as long as the previous one is in a surprise-removed state. The // previous one would be in that state after the device has been // physically removed, if somebody has an handle open to it. // if (SerialNo == pdoData->SerialNo) { unique = FALSE; status = STATUS_INVALID_PARAMETER; break; } } if (unique) { // // Create a new child device. It is OK to create and add a child while // the list locked for enumeration. The enumeration lock applies only // to enumeration, not addition or removal. // status = Bus_CreatePdo(Device, HardwareIds, SerialNo); } WdfFdoUnlockStaticChildListFromIteration(Device); WdfWaitLockRelease(deviceData->ChildLock); return status; }
NTSTATUS Bus_EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: Bus_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 toaster bus. Arguments: Driver - Handle to a framework driver object created in DriverEntry DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. Return Value: NTSTATUS --*/ { WDF_IO_QUEUE_CONFIG queueConfig; WDF_OBJECT_ATTRIBUTES attributes; NTSTATUS status; WDFDEVICE device; PFDO_DEVICE_DATA deviceData; PNP_BUS_INFORMATION busInfo; WDFQUEUE queue; UNREFERENCED_PARAMETER(Driver); PAGED_CODE (); KdPrint(("Bus_EvtDeviceAdd: 0x%p\n", Driver)); // // Initialize all the properties specific to the device. // Framework has default values for the one that are not // set explicitly here. So please read the doc and make sure // you are okay with the defaults. // WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); WdfDeviceInitSetExclusive(DeviceInit, TRUE); // // Initialize attributes structure to specify size and accessor function // for storing device context. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FDO_DEVICE_DATA); // // Create a framework device object. In response to this call, framework // creates a WDM deviceobject. // status = WdfDeviceCreate(&DeviceInit, &attributes, &device); if (!NT_SUCCESS(status)) { return status; } // // Get the device context. // deviceData = FdoGetData(device); WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.ParentObject = device; // // Purpose of this lock is documented in Bus_PlugInDevice routine below. // status = WdfWaitLockCreate(&attributes, &deviceData->ChildLock); if (!NT_SUCCESS(status)) { return status; } // // Configure a default queue so that requests that are not // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto // other queues get dispatched here. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &queueConfig, WdfIoQueueDispatchParallel ); queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl; // // By default, Static Driver Verifier (SDV) displays a warning if it // doesn't find the EvtIoStop callback on a power-managed queue. // The 'assume' below causes SDV to suppress this warning. If the driver // has not explicitly set PowerManaged to WdfFalse, the framework creates // power-managed queues when the device is not a filter driver. Normally // the EvtIoStop is required for power-managed queues, but for this driver // it is not needed b/c the driver doesn't hold on to the requests or // forward them to other drivers. This driver completes the requests // directly in the queue's handlers. If the EvtIoStop callback is not // implemented, the framework waits for all driver-owned requests to be // done before moving in the Dx/sleep states or before removing the // device, which is the correct behavior for this type of driver. // If the requests were taking an indeterminate amount of time to complete, // or if the driver forwarded the requests to a lower driver/another stack, // the queue should have an EvtIoStop/EvtIoResume. // __analysis_assume(queueConfig.EvtIoStop != 0); status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue ); __analysis_assume(queueConfig.EvtIoStop == 0); if (!NT_SUCCESS(status)) { KdPrint(("WdfIoQueueCreate failed with status 0x%x\n", status)); return status; } // // Create device interface for this device. The interface will be // enabled by the framework when we return from StartDevice successfully. // status = WdfDeviceCreateDeviceInterface( device, &GUID_DEVINTERFACE_BUSENUM_TOASTER, NULL // No Reference String ); if (!NT_SUCCESS(status)) { return status; } // // This value is used in responding to the IRP_MN_QUERY_BUS_INFORMATION // for the child devices. This is an optional information provided to // uniquely idenitfy the bus the device is connected. // busInfo.BusTypeGuid = GUID_DEVCLASS_TOASTER; busInfo.LegacyBusType = PNPBus; busInfo.BusNumber = 0; WdfDeviceSetBusInformationForChildren(device, &busInfo); status = Bus_WmiRegistration(device); if (!NT_SUCCESS(status)) { return status; } status = Bus_DoStaticEnumeration(device); return status; }
VOID NICWatchDogEvtTimerFunc( IN WDFTIMER Timer ) /*++ Routine Description: This DPC is used to do both link detection during hardware init and after that for hardware hang detection. Arguments: Return Value: None --*/ { PFDO_DATA FdoData = NULL; LARGE_INTEGER DueTime; NTSTATUS status = STATUS_SUCCESS; FdoData = FdoGetData(WdfTimerGetParentObject(Timer)); DueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY; if(!FdoData->CheckForHang){ // // We are still doing link detection // status = NICLinkDetection(FdoData); if(status == STATUS_PENDING) { // Wait for 100 ms FdoData->bLinkDetectionWait = TRUE; DueTime.QuadPart = NIC_LINK_DETECTION_DELAY; }else { FdoData->CheckForHang = TRUE; } }else { // // Link detection is over, let us check to see // if the hardware is stuck. // if(NICCheckForHang(FdoData)){ status = NICReset(FdoData); if(!NT_SUCCESS(status)){ goto Exit; } } } WdfTimerStart(FdoData->WatchDogTimer, // Timer DueTime.QuadPart // DueTime ); return; Exit: TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "WatchDogTimer is exiting %x\n", status); return; }
NTSTATUS Bus_WmiRegistration( WDFDEVICE Device ) /*++ Routine Description Registers with WMI as a data provider for this instance of the device --*/ { WDF_WMI_PROVIDER_CONFIG providerConfig; WDF_WMI_INSTANCE_CONFIG instanceConfig; PFDO_DEVICE_DATA deviceData; NTSTATUS status; DECLARE_CONST_UNICODE_STRING(busRsrcName, BUSRESOURCENAME); PAGED_CODE(); deviceData = FdoGetData(Device); // // Register WMI classes. // First specify the resource name which contain the binary mof resource. // status = WdfDeviceAssignMofResourceName(Device, &busRsrcName); if (!NT_SUCCESS(status)) { return status; } WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig, &ToasterBusInformation_GUID); providerConfig.MinInstanceBufferSize = sizeof(TOASTER_BUS_WMI_STD_DATA); // // You would want to create a WDFWMIPROVIDER handle separately if you are // going to dynamically create instances on the provider. Since we are // statically creating one instance, there is no need to create the provider // handle. // WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig, &providerConfig); // // By setting Regsiter to TRUE, we tell the framework to create a provider // as part of the Instance creation call. This eliminates the need to // call WdfWmiProviderRegister. // instanceConfig.Register = TRUE; instanceConfig.EvtWmiInstanceQueryInstance = Bus_EvtStdDataQueryInstance; instanceConfig.EvtWmiInstanceSetInstance = Bus_EvtStdDataSetInstance; instanceConfig.EvtWmiInstanceSetItem = Bus_EvtStdDataSetItem; status = WdfWmiInstanceCreate( Device, &instanceConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE ); if (NT_SUCCESS(status)) { deviceData->StdToasterBusData.ErrorCount = 0; } return status; }
NTSTATUS Bus_EvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: Bus_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 toaster bus. Arguments: Driver - Handle to a framework driver object created in DriverEntry DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure. Return Value: NTSTATUS --*/ { WDF_CHILD_LIST_CONFIG config; WDF_OBJECT_ATTRIBUTES fdoAttributes; NTSTATUS status; WDFDEVICE device; WDF_IO_QUEUE_CONFIG queueConfig; PNP_BUS_INFORMATION busInfo; PFDO_DEVICE_DATA deviceData; WDFQUEUE queue; UNREFERENCED_PARAMETER(Driver); PAGED_CODE (); KdPrint(("Bus_EvtDeviceAdd: 0x%p\n", Driver)); // // Initialize all the properties specific to the device. // Framework has default values for the one that are not // set explicitly here. So please read the doc and make sure // you are okay with the defaults. // WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); WdfDeviceInitSetExclusive(DeviceInit, TRUE); // // Since this is pure software bus enumerator, we don't have to register for // any PNP/Power callbacks. Framework will take the default action for // all the PNP and Power IRPs. // // // WDF_ DEVICE_LIST_CONFIG describes how the framework should handle // dynamic child enumeration on behalf of the driver writer. // Since we are a bus driver, we need to specify identification description // for our child devices. This description will serve as the identity of our // child device. Since the description is opaque to the framework, we // have to provide bunch of callbacks to compare, copy, or free // any other resources associated with the description. // WDF_CHILD_LIST_CONFIG_INIT(&config, sizeof(PDO_IDENTIFICATION_DESCRIPTION), Bus_EvtDeviceListCreatePdo // callback to create a child device. ); // // This function pointer will be called when the framework needs to copy a // identification description from one location to another. An implementation // of this function is only necessary if the description contains description // relative pointer values (like LIST_ENTRY for instance) . // If set to NULL, the framework will use RtlCopyMemory to copy an identification . // description. In this sample, it's not required to provide these callbacks. // they are added just for illustration. // config.EvtChildListIdentificationDescriptionDuplicate = Bus_EvtChildListIdentificationDescriptionDuplicate; // // This function pointer will be called when the framework needs to compare // two identificaiton descriptions. If left NULL a call to RtlCompareMemory // will be used to compare two identificaiton descriptions. // config.EvtChildListIdentificationDescriptionCompare = Bus_EvtChildListIdentificationDescriptionCompare; // // This function pointer will be called when the framework needs to free a // identification description. An implementation of this function is only // necessary if the description contains dynamically allocated memory // (by the driver writer) that needs to be freed. The actual identification // description pointer itself will be freed by the framework. // config.EvtChildListIdentificationDescriptionCleanup = Bus_EvtChildListIdentificationDescriptionCleanup; // // Tell the framework to use the built-in childlist to track the state // of the device based on the configuration we just created. // WdfFdoInitSetDefaultChildListConfig(DeviceInit, &config, WDF_NO_OBJECT_ATTRIBUTES); // // Initialize attributes structure to specify size and accessor function // for storing device context. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DEVICE_DATA); // // Create a framework device object. In response to this call, framework // creates a WDM deviceobject and attach to the PDO. // status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device); if (!NT_SUCCESS(status)) { KdPrint(("Error creating device 0x%x\n", status)); return status; } // // Configure a default queue so that requests that are not // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto // other queues get dispatched here. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &queueConfig, WdfIoQueueDispatchParallel ); queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl; status = WdfIoQueueCreate( device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue ); if (!NT_SUCCESS(status)) { KdPrint(("WdfIoQueueCreate failed status 0x%x\n", status)); return status; } // // Get the device context. // deviceData = FdoGetData(device); // // Create device interface for this device. The interface will be // enabled by the framework when we return from StartDevice successfully. // Clients of this driver will open this interface and send ioctls. // status = WdfDeviceCreateDeviceInterface( device, &GUID_DEVINTERFACE_BUSENUM_TOASTER, NULL // No Reference String. If you provide one it will appended to the ); // symbolic link. Some drivers register multiple interfaces for the same device // and use the reference string to distinguish between them if (!NT_SUCCESS(status)) { return status; } // // This value is used in responding to the IRP_MN_QUERY_BUS_INFORMATION // for the child devices. This is an optional information provided to // uniquely idenitfy the bus the device is connected. // busInfo.BusTypeGuid = GUID_DEVCLASS_TOASTER; busInfo.LegacyBusType = PNPBus; busInfo.BusNumber = 0; WdfDeviceSetBusInformationForChildren(device, &busInfo); status = Bus_WmiRegistration(device); if (!NT_SUCCESS(status)) { return status; } // // Check the registry to see if we need to enumerate child devices during // start. // status = Bus_DoStaticEnumeration(device); return status; }