615 lines
17 KiB
C
615 lines
17 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1996 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
nbfpnp.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains code which allocates and initializes all data
|
||
|
structures needed to activate a plug and play binding. It also informs
|
||
|
tdi (and thus nbf clients) of new devices and protocol addresses.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Jim McNelis (jimmcn) 1-Jan-1996
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#ifdef RASAUTODIAL
|
||
|
|
||
|
LONG NumberOfBinds = 0;
|
||
|
|
||
|
VOID
|
||
|
NbfAcdBind();
|
||
|
|
||
|
VOID
|
||
|
NbfAcdUnbind();
|
||
|
|
||
|
#endif // RASAUTODIAL
|
||
|
|
||
|
// PnP-Power Declarations
|
||
|
|
||
|
VOID
|
||
|
NbfPnPEventDispatch(
|
||
|
IN PVOID NetPnPEvent
|
||
|
);
|
||
|
|
||
|
VOID
|
||
|
NbfPnPEventComplete(
|
||
|
IN PNET_PNP_EVENT NetPnPEvent,
|
||
|
IN NTSTATUS retVal
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
NbfPnPBindsComplete(
|
||
|
IN PDEVICE_CONTEXT DeviceContext,
|
||
|
IN PNET_PNP_EVENT NetPnPEvent
|
||
|
);
|
||
|
|
||
|
// PnP Handler Routines
|
||
|
|
||
|
VOID
|
||
|
NbfProtocolBindAdapter(
|
||
|
OUT PNDIS_STATUS NdisStatus,
|
||
|
IN NDIS_HANDLE BindContext,
|
||
|
IN PNDIS_STRING DeviceName,
|
||
|
IN PVOID SystemSpecific1,
|
||
|
IN PVOID SystemSpecific2
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine activates a transport binding and exposes the new device
|
||
|
and associated addresses to transport clients. This is done by reading
|
||
|
the registry, and performing any one time initialization of the transport
|
||
|
and then natching the device to bind to with the linkage information from
|
||
|
the registry. If we have a match for that device the bind will be
|
||
|
performed.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NdisStatus - The status of the bind.
|
||
|
|
||
|
BindContext - A context used for NdisCompleteBindAdapter() if
|
||
|
STATUS_PENDING is returned.
|
||
|
|
||
|
DeviceName - The name of the device that we are binding with.
|
||
|
|
||
|
SystemSpecific1 - Unused (a pointer to an NDIS_STRING to use with
|
||
|
NdisOpenProtocolConfiguration. This is not used by nbf
|
||
|
since there is no adapter specific information when
|
||
|
configuring the protocol via the registry. Passed to
|
||
|
NbfInitializeOneDeviceContext for possible future use)
|
||
|
|
||
|
SystemSpecific2 - Passed to NbfInitializeOneDeviceContext to be used
|
||
|
in a call to TdiRegisterNetAddress
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PUNICODE_STRING ExportName;
|
||
|
UNICODE_STRING ExportString;
|
||
|
ULONG i, j, k;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
#if DBG
|
||
|
// We can never be called at DISPATCH or above
|
||
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
|
||
|
{
|
||
|
DbgBreakPoint();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint1 ("ENTER NbfProtocolBindAdapter for %S\n", DeviceName->Buffer);
|
||
|
}
|
||
|
|
||
|
if (NbfConfig == NULL) {
|
||
|
//
|
||
|
// This allocates the CONFIG_DATA structure and returns
|
||
|
// it in NbfConfig.
|
||
|
//
|
||
|
|
||
|
status = NbfConfigureTransport(&NbfRegistryPath, &NbfConfig);
|
||
|
|
||
|
if (!NT_SUCCESS (status)) {
|
||
|
PANIC (" Failed to initialize transport, Nbf binding failed.\n");
|
||
|
*NdisStatus = NDIS_STATUS_RESOURCES;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
//
|
||
|
// Allocate the debugging tables.
|
||
|
//
|
||
|
|
||
|
NbfConnectionTable = (PVOID *)ExAllocatePoolWithTag(NonPagedPool,
|
||
|
sizeof(PVOID) *
|
||
|
(NbfConfig->InitConnections + 2 +
|
||
|
NbfConfig->InitRequests + 2 +
|
||
|
NbfConfig->InitUIFrames + 2 +
|
||
|
NbfConfig->InitPackets + 2 +
|
||
|
NbfConfig->InitLinks + 2 +
|
||
|
NbfConfig->InitAddressFiles + 2 +
|
||
|
NbfConfig->InitAddresses + 2),
|
||
|
NBF_MEM_TAG_CONNECTION_TABLE);
|
||
|
|
||
|
ASSERT (NbfConnectionTable);
|
||
|
|
||
|
NbfRequestTable = NbfConnectionTable + (NbfConfig->InitConnections + 2);
|
||
|
NbfUiFrameTable = NbfRequestTable + (NbfConfig->InitRequests + 2);
|
||
|
NbfSendPacketTable = NbfUiFrameTable + (NbfConfig->InitUIFrames + 2);
|
||
|
NbfLinkTable = NbfSendPacketTable + (NbfConfig->InitPackets + 2);
|
||
|
NbfAddressFileTable = NbfLinkTable + (NbfConfig->InitLinks + 2);
|
||
|
NbfAddressTable = NbfAddressFileTable +
|
||
|
(NbfConfig->InitAddressFiles + 2);
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Loop through all the adapters that are in the configuration
|
||
|
// information structure (this is the initial cache) until we
|
||
|
// find the one that NDIS is calling Protocol bind adapter for.
|
||
|
//
|
||
|
|
||
|
for (j = 0; j < NbfConfig->NumAdapters; j++ ) {
|
||
|
|
||
|
if (NdisEqualString(DeviceName, &NbfConfig->Names[j], TRUE)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j < NbfConfig->NumAdapters) {
|
||
|
|
||
|
// We found the bind to export mapping in initial cache
|
||
|
|
||
|
ExportName = &NbfConfig->Names[NbfConfig->DevicesOffset + j];
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
|
||
|
NbfPrint1("\nNot In Initial Cache = %08x\n\n", DeviceName->Buffer);
|
||
|
|
||
|
NbfPrint0("Bind Names in Initial Cache: \n");
|
||
|
|
||
|
for (k = 0; k < NbfConfig->NumAdapters; k++)
|
||
|
{
|
||
|
NbfPrint3("Config[%2d]: @ %08x, %75S\n",
|
||
|
k, &NbfConfig->Names[k],
|
||
|
NbfConfig->Names[k].Buffer);
|
||
|
}
|
||
|
|
||
|
NbfPrint0("Export Names in Initial Cache: \n");
|
||
|
|
||
|
for (k = 0; k < NbfConfig->NumAdapters; k++)
|
||
|
{
|
||
|
NbfPrint3("Config[%2d]: @ %08x, %75S\n",
|
||
|
k, &NbfConfig->Names[NbfConfig->DevicesOffset + k],
|
||
|
NbfConfig->Names[NbfConfig->DevicesOffset + k].Buffer);
|
||
|
}
|
||
|
|
||
|
NbfPrint0("\n\n");
|
||
|
}
|
||
|
|
||
|
ExportName = &ExportString;
|
||
|
|
||
|
//
|
||
|
// We have not found the name in the initial registry info;
|
||
|
// Read the registry and check if a new binding appeared...
|
||
|
//
|
||
|
|
||
|
*NdisStatus = NbfGetExportNameFromRegistry(&NbfRegistryPath,
|
||
|
DeviceName,
|
||
|
ExportName
|
||
|
);
|
||
|
if (!NT_SUCCESS (*NdisStatus))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NbfInitializeOneDeviceContext(NdisStatus,
|
||
|
NbfDriverObject,
|
||
|
NbfConfig,
|
||
|
DeviceName,
|
||
|
ExportName,
|
||
|
SystemSpecific1,
|
||
|
SystemSpecific2
|
||
|
);
|
||
|
|
||
|
// Check if we need to de-allocate the ExportName buffer
|
||
|
|
||
|
if (ExportName == &ExportString)
|
||
|
{
|
||
|
ExFreePool(ExportName->Buffer);
|
||
|
}
|
||
|
|
||
|
if (*NdisStatus == NDIS_STATUS_SUCCESS) {
|
||
|
|
||
|
if (InterlockedIncrement(&NumberOfBinds) == 1) {
|
||
|
|
||
|
#ifdef RASAUTODIAL
|
||
|
|
||
|
//
|
||
|
// This is the first successful open.
|
||
|
//
|
||
|
#if DBG
|
||
|
DbgPrint("Calling NbfAcdBind()\n");
|
||
|
#endif
|
||
|
//
|
||
|
// Get the automatic connection driver entry points.
|
||
|
//
|
||
|
|
||
|
NbfAcdBind();
|
||
|
|
||
|
#endif // RASAUTODIAL
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint2 ("LEAVE NbfProtocolBindAdapter for %S with Status %08x\n",
|
||
|
DeviceName->Buffer, *NdisStatus);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
NbfProtocolUnbindAdapter(
|
||
|
OUT PNDIS_STATUS NdisStatus,
|
||
|
IN NDIS_HANDLE ProtocolBindContext,
|
||
|
IN PNDIS_HANDLE UnbindContext
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine deactivates a transport binding. Before it does this, it
|
||
|
indicates to all clients above, that the device is going away. Clients
|
||
|
are expected to close all open handles to the device.
|
||
|
|
||
|
Then the device is pulled out of the list of NBF devices, and all
|
||
|
resources reclaimed. Any connections, address files etc, that the
|
||
|
client has cleaned up are forcibly cleaned out at this point. Any
|
||
|
outstanding requests are completed (with a status). Any future
|
||
|
requests are automatically invalid as they use obsolete handles.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NdisStatus - The status of the bind.
|
||
|
|
||
|
ProtocolBindContext - the context from the openadapter call
|
||
|
|
||
|
UnbindContext - A context for async unbinds.
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PDEVICE_CONTEXT DeviceContext;
|
||
|
PTP_ADDRESS Address;
|
||
|
NTSTATUS status;
|
||
|
KIRQL oldirql;
|
||
|
PLIST_ENTRY p;
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
// We can never be called at DISPATCH or above
|
||
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
|
||
|
{
|
||
|
DbgBreakPoint();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Get the device context for the adapter being unbound
|
||
|
DeviceContext = (PDEVICE_CONTEXT) ProtocolBindContext;
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint1 ("ENTER NbfProtocolUnbindAdapter for %S\n", DeviceContext->DeviceName);
|
||
|
}
|
||
|
|
||
|
// Remove creation ref if it has not already been removed,
|
||
|
// after telling TDI and its clients that we'r going away.
|
||
|
// This flag also helps prevent any more TDI indications
|
||
|
// of deregister addr/devobj - after the 1st one succeeds.
|
||
|
if (InterlockedExchange(&DeviceContext->CreateRefRemoved, TRUE) == FALSE) {
|
||
|
|
||
|
// Assume upper layers clean up by closing connections
|
||
|
// when we deregister all addresses and device object,
|
||
|
// but this can happen asynchronously, after we return
|
||
|
// from the (asynchronous) TdiDeregister.. calls below
|
||
|
|
||
|
// Inform TDI by deregistering the reserved netbios address
|
||
|
*NdisStatus = TdiDeregisterNetAddress(DeviceContext->ReservedAddressHandle);
|
||
|
|
||
|
if (!NT_SUCCESS (*NdisStatus)) {
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint1("No success deregistering this address,STATUS = %08X\n",*NdisStatus);
|
||
|
}
|
||
|
|
||
|
// this can never happen
|
||
|
ASSERT(FALSE);
|
||
|
|
||
|
// In case it happens, this allows a redo of the unbind
|
||
|
DeviceContext->CreateRefRemoved = FALSE;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Inform TDI (and its clients) that device is going away
|
||
|
*NdisStatus = TdiDeregisterDeviceObject(DeviceContext->TdiDeviceHandle);
|
||
|
|
||
|
if (!NT_SUCCESS (*NdisStatus)) {
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint1("No success deregistering device object,STATUS = %08X\n",*NdisStatus);
|
||
|
}
|
||
|
|
||
|
// This can never happen
|
||
|
ASSERT(FALSE);
|
||
|
|
||
|
// In case it happens, this allows a redo of the unbind
|
||
|
DeviceContext->CreateRefRemoved = FALSE;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Clear away the association with the underlying PDO object
|
||
|
DeviceContext->PnPContext = NULL;
|
||
|
|
||
|
// Stop all the internal timers - this'll clear timer refs
|
||
|
NbfStopTimerSystem(DeviceContext);
|
||
|
|
||
|
// Cleanup the Ndis Binding as it is not useful on return
|
||
|
// from this function - do not try to use it after this
|
||
|
NbfCloseNdis(DeviceContext);
|
||
|
|
||
|
// BUG BUG -- probable race condition with timer callbacks
|
||
|
// Do we wait for some time in case a timer func gets in ?
|
||
|
|
||
|
// Removing creation reference means that once all handles
|
||
|
// r closed,device will automatically be garbage-collected
|
||
|
NbfDereferenceDeviceContext ("Unload", DeviceContext, DCREF_CREATION);
|
||
|
|
||
|
if (InterlockedDecrement(&NumberOfBinds) == 0) {
|
||
|
|
||
|
#ifdef RASAUTODIAL
|
||
|
|
||
|
//
|
||
|
// This is a successful close of last adapter
|
||
|
//
|
||
|
#if DBG
|
||
|
DbgPrint("Calling NbfAcdUnbind()\n");
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Unbind from the automatic connection driver.
|
||
|
//
|
||
|
|
||
|
NbfAcdUnbind();
|
||
|
|
||
|
#endif // RASAUTODIAL
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
// Ignore any duplicate Unbind Indications from NDIS layer
|
||
|
*NdisStatus = NDIS_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
IF_NBFDBG (NBF_DEBUG_PNP) {
|
||
|
NbfPrint2 ("LEAVE NbfProtocolUnbindAdapter for %S with Status %08x\n",
|
||
|
DeviceContext->DeviceName, *NdisStatus);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NDIS_STATUS
|
||
|
NbfProtocolPnPEventHandler(
|
||
|
IN NDIS_HANDLE ProtocolBindContext,
|
||
|
IN PNET_PNP_EVENT NetPnPEvent
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine queues a work item to invoke the actual PnP
|
||
|
event dispatcher. This asyncronous mechanism is to allow
|
||
|
NDIS to signal PnP events to other bindings in parallel.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
ProtocolBindContext - the context from the openadapter call
|
||
|
|
||
|
NetPnPEvent - kind of PnP event and its parameters
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
STATUS_PENDING (or) an error code
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PNET_PNP_EVENT_RESERVED NetPnPReserved;
|
||
|
PWORK_QUEUE_ITEM PnPWorkItem;
|
||
|
|
||
|
PnPWorkItem = (PWORK_QUEUE_ITEM)ExAllocatePoolWithTag(
|
||
|
NonPagedPool,
|
||
|
sizeof (WORK_QUEUE_ITEM),
|
||
|
NBF_MEM_TAG_WORK_ITEM);
|
||
|
|
||
|
if (PnPWorkItem == NULL)
|
||
|
{
|
||
|
return NDIS_STATUS_RESOURCES;
|
||
|
}
|
||
|
|
||
|
NetPnPReserved = (PNET_PNP_EVENT_RESERVED)NetPnPEvent->TransportReserved;
|
||
|
NetPnPReserved->PnPWorkItem = PnPWorkItem;
|
||
|
NetPnPReserved->DeviceContext = (PDEVICE_CONTEXT) ProtocolBindContext;
|
||
|
|
||
|
ExInitializeWorkItem(
|
||
|
PnPWorkItem,
|
||
|
NbfPnPEventDispatch,
|
||
|
NetPnPEvent);
|
||
|
|
||
|
ExQueueWorkItem(PnPWorkItem, CriticalWorkQueue);
|
||
|
|
||
|
return NDIS_STATUS_PENDING;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
NbfPnPEventDispatch(
|
||
|
IN PVOID NetPnPEvent
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine dispatches all PnP events for the NBF transport.
|
||
|
The event is dispatched to the proper PnP event handler, and
|
||
|
the events are indicated to the transport clients using TDI.
|
||
|
|
||
|
These PnP events can trigger state changes that affect the
|
||
|
device behavior ( like transitioning to low power state ).
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
NetPnPEvent - kind of PnP event and its parameters
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PNET_PNP_EVENT_RESERVED NetPnPReserved;
|
||
|
PDEVICE_CONTEXT DeviceContext;
|
||
|
UNICODE_STRING DeviceString;
|
||
|
PTDI_PNP_CONTEXT tdiPnPContext1;
|
||
|
PTDI_PNP_CONTEXT tdiPnPContext2;
|
||
|
NDIS_STATUS retVal;
|
||
|
|
||
|
// Retrieve the transport information block in event
|
||
|
NetPnPReserved = (PNET_PNP_EVENT_RESERVED)((PNET_PNP_EVENT)NetPnPEvent)->TransportReserved;
|
||
|
|
||
|
// Free the memory allocated for this work item itself
|
||
|
ExFreePool(NetPnPReserved->PnPWorkItem);
|
||
|
|
||
|
// Get the device context for the adapter being unbound
|
||
|
DeviceContext = NetPnPReserved->DeviceContext;
|
||
|
|
||
|
// In case everything goes ok, we return an NDIS_SUCCESS
|
||
|
retVal = STATUS_SUCCESS;
|
||
|
|
||
|
// Dispatch the PnP Event to the appropriate PnP handler
|
||
|
switch (((PNET_PNP_EVENT)NetPnPEvent)->NetEvent)
|
||
|
{
|
||
|
case NetEventReconfigure:
|
||
|
case NetEventCancelRemoveDevice:
|
||
|
case NetEventQueryRemoveDevice:
|
||
|
case NetEventQueryPower:
|
||
|
case NetEventSetPower:
|
||
|
case NetEventPnPCapabilities:
|
||
|
break;
|
||
|
|
||
|
case NetEventBindsComplete:
|
||
|
retVal = NbfPnPBindsComplete(DeviceContext, NetPnPEvent);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT( FALSE );
|
||
|
}
|
||
|
|
||
|
if ( retVal == STATUS_SUCCESS )
|
||
|
{
|
||
|
if (DeviceContext != NULL)
|
||
|
{
|
||
|
RtlInitUnicodeString(&DeviceString, DeviceContext->DeviceName);
|
||
|
tdiPnPContext1 = tdiPnPContext2 = NULL;
|
||
|
|
||
|
// Notify our TDI clients about this PNP event
|
||
|
retVal = TdiPnPPowerRequest(&DeviceString,
|
||
|
NetPnPEvent,
|
||
|
tdiPnPContext1,
|
||
|
tdiPnPContext2,
|
||
|
NbfPnPEventComplete);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (retVal != STATUS_PENDING)
|
||
|
{
|
||
|
NdisCompletePnPEvent(retVal, (NDIS_HANDLE)DeviceContext, NetPnPEvent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// PnP Complete Handler
|
||
|
//
|
||
|
VOID
|
||
|
NbfPnPEventComplete(
|
||
|
IN PNET_PNP_EVENT NetPnPEvent,
|
||
|
IN NTSTATUS retVal
|
||
|
)
|
||
|
{
|
||
|
PNET_PNP_EVENT_RESERVED NetPnPReserved;
|
||
|
PDEVICE_CONTEXT DeviceContext;
|
||
|
|
||
|
// Retrieve the transport information block in event
|
||
|
NetPnPReserved = (PNET_PNP_EVENT_RESERVED)NetPnPEvent->TransportReserved;
|
||
|
|
||
|
// Get the device context for the adapter being unbound
|
||
|
DeviceContext = NetPnPReserved->DeviceContext;
|
||
|
|
||
|
NdisCompletePnPEvent(retVal, (NDIS_HANDLE)DeviceContext, NetPnPEvent);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// PnP Handler Dispatches
|
||
|
//
|
||
|
|
||
|
NTSTATUS
|
||
|
NbfPnPBindsComplete(
|
||
|
IN PDEVICE_CONTEXT DeviceContext,
|
||
|
IN PNET_PNP_EVENT NetPnPEvent
|
||
|
)
|
||
|
{
|
||
|
NDIS_STATUS retVal;
|
||
|
|
||
|
ASSERT(DeviceContext == NULL);
|
||
|
|
||
|
retVal = TdiProviderReady(NbfProviderHandle);
|
||
|
|
||
|
ASSERT(retVal == STATUS_SUCCESS);
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|