/*++ 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; }