/*++ Copyright(c) 1999-2000 Microsoft Corporation Module Name: brdgprot.c Abstract: Ethernet MAC level bridge. Protocol section Author: Mark Aiken (original bridge by Jameel Hyder) Environment: Kernel mode driver Revision History: Sept 1999 - Original version Feb 2000 - Overhaul --*/ #pragma warning( push, 3 ) #include #pragma warning( pop ) #include #include "bridge.h" #include "brdgprot.h" #include "brdgmini.h" #include "brdgfwd.h" #include "brdgtbl.h" #include "brdgbuf.h" #include "brdgctl.h" #include "brdgsta.h" #include "brdgcomp.h" // =========================================================================== // // GLOBALS // // =========================================================================== // NDIS handle for our identity as a protocol NDIS_HANDLE gProtHandle = NULL; NDIS_MEDIUM gMediumArray[1] = { NdisMedium802_3 // Ethernet only, can add other media later }; // The adapter list and associated lock PADAPT gAdapterList = {0}; NDIS_RW_LOCK gAdapterListLock; // Whether we have called the miniport sections BindsComplete() function yet // 0 == no, 1 == yes LONG gHaveInitedMiniport = 0L; // A lock for all the adapters' link speed and media connect characteristics NDIS_RW_LOCK gAdapterCharacteristicsLock; // Number of adapters. Doesn't change if a lock is held on gAdapterListLock ULONG gNumAdapters = 0L; // Name of the registry value that forces an adapter into compatibility mode NDIS_STRING gForceCompatValueName = NDIS_STRING_CONST("ForceCompatibilityMode"); #if DBG // Boolean to force all adapters into compatibility mode BOOLEAN gAllAdaptersCompat = FALSE; const PWCHAR gForceAllCompatPropertyName = L"ForceAllToCompatibilityMode"; #endif // =========================================================================== // // PRIVATE PROTOTYPES // // =========================================================================== VOID BrdgProtOpenAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus ); VOID BrdgProtCloseAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status ); VOID BrdgProtStatus( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID StatusBuffer, IN UINT StatusBufferSize ); VOID BrdgProtStatusComplete( IN NDIS_HANDLE ProtocolBindingContext ); VOID BrdgProtReceiveComplete( IN NDIS_HANDLE ProtocolBindingContext ); VOID BrdgProtBindAdapter( OUT PNDIS_STATUS Status, IN NDIS_HANDLE BindContext, IN PNDIS_STRING DeviceName, IN PVOID SystemSpecific1, IN PVOID SystemSpecific2 ); VOID BrdgProtUnbindAdapter( OUT PNDIS_STATUS pStatus, IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE UnbindContext ); NDIS_STATUS BrdgProtPnPEvent( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_PNP_EVENT NetPnPEvent ); VOID BrdgProtUnload(VOID); UINT BrdgProtCoReceive( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET Packet ); // =========================================================================== // // INLINES / MACROS // // =========================================================================== // // Signals the queue-draining threads that there has been a change in the // adapter list // __forceinline VOID BrdgProtSignalAdapterListChange() { INT i; for( i = 0; i < KeNumberProcessors; i++ ) { KeSetEvent( &gThreadsCheckAdapters[i], EVENT_INCREMENT, FALSE ); } } // // Writes an entry to the event log that indicates an error on a specific // adapter. // // Can be used after we have successfully retrieved the adapter's friendly name. // __inline VOID BrdgProtLogAdapterErrorFriendly( IN NDIS_STATUS ErrorCode, IN PADAPT pAdapt, IN NDIS_STATUS ErrorStatus ) { PWCHAR StringPtr = pAdapt->DeviceDesc.Buffer; NdisWriteEventLogEntry( gDriverObject, ErrorCode, 0, 1, &StringPtr, sizeof(NDIS_STATUS), &ErrorStatus ); } // // Writes an entry to the event log that indicates an error on a specific // adapter. // // Reports the adapter's device name, so can be used before we have successfully retrieved // the adapter's friendly name. // __inline VOID BrdgProtLogAdapterError( IN NDIS_STATUS ErrorCode, IN PADAPT pAdapt, IN NDIS_STATUS ErrorStatus ) { PWCHAR StringPtr = pAdapt->DeviceName.Buffer; NdisWriteEventLogEntry( gDriverObject, ErrorCode, 0, 1, &StringPtr, sizeof(NDIS_STATUS), &ErrorStatus ); } // Removes all references to an adapter from our various tables __forceinline VOID BrdgProtScrubAdapter( IN PADAPT pAdapt ) { BrdgTblScrubAdapter( pAdapt ); BrdgCompScrubAdapter( pAdapt ); } // Returns the number of adapters that are currently bridged. ULONG BrdgProtGetAdapterCount() { return gNumAdapters; } // =========================================================================== // // PUBLIC FUNCTIONS // // =========================================================================== NTSTATUS BrdgProtDriverInit() /*++ Routine Description: Initialization routine for the protocol section. Must be called at PASSIVE level because we call NdisRegisterProtocol(). Arguments: None Return Value: STATUS_SUCCESS to continue initialization or an error code to abort driver startup --*/ { NDIS_PROTOCOL_CHARACTERISTICS ProtChars; NDIS_STATUS NdisStatus; NDIS_STRING Name; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); // Initialize locks NdisInitializeReadWriteLock( &gAdapterListLock ); NdisInitializeReadWriteLock( &gAdapterCharacteristicsLock ); // // Register the protocol. // NdisZeroMemory(&ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); ProtChars.MajorNdisVersion = 5; ProtChars.MinorNdisVersion = 0; // // Make sure the protocol-name matches the service-name under which this protocol is installed. // This is needed to ensure that NDIS can correctly determine the binding and call us to bind // to miniports below. // NdisInitUnicodeString(&Name, PROTOCOL_NAME); ProtChars.Name = Name; ProtChars.OpenAdapterCompleteHandler = BrdgProtOpenAdapterComplete; ProtChars.CloseAdapterCompleteHandler = BrdgProtCloseAdapterComplete; ProtChars.RequestCompleteHandler = BrdgProtRequestComplete; ProtChars.ReceiveCompleteHandler = BrdgProtReceiveComplete; ProtChars.StatusHandler = BrdgProtStatus; ProtChars.StatusCompleteHandler = BrdgProtStatusComplete; ProtChars.BindAdapterHandler = BrdgProtBindAdapter; ProtChars.UnbindAdapterHandler = BrdgProtUnbindAdapter; ProtChars.PnPEventHandler = BrdgProtPnPEvent; ProtChars.UnloadHandler = BrdgProtUnload; ProtChars.CoReceivePacketHandler = BrdgProtCoReceive; // // These entry points are provided by the forwarding engine // ProtChars.ReceiveHandler = BrdgFwdReceive; ProtChars.TransferDataCompleteHandler = BrdgFwdTransferComplete; ProtChars.ReceivePacketHandler = BrdgFwdReceivePacket; ProtChars.SendCompleteHandler = BrdgFwdSendComplete; // Register ourselves NdisRegisterProtocol(&NdisStatus, &gProtHandle, &ProtChars, sizeof(NDIS_PROTOCOL_CHARACTERISTICS)); if (NdisStatus != NDIS_STATUS_SUCCESS) { // This is a fatal error. Log it. NdisWriteEventLogEntry( gDriverObject, EVENT_BRIDGE_PROTOCOL_REGISTER_FAILED, 0, 0, NULL, sizeof(NDIS_STATUS), &NdisStatus ); DBGPRINT(PROT, ("Failed to register the protocol driver with NDIS: %08x\n", NdisStatus)); return STATUS_UNSUCCESSFUL; } #if DBG { NTSTATUS Status; ULONG Value; // Check if we're supposed to force all adapters into compat mode Status = BrdgReadRegDWord( &gRegistryPath, gForceAllCompatPropertyName, &Value ); if( (Status == STATUS_SUCCESS) && (Value != 0L) ) { DBGPRINT(COMPAT, ("FORCING ALL ADAPTERS TO COMPATIBILITY MODE!\n")); gAllAdaptersCompat = TRUE; } } #endif return STATUS_SUCCESS; } VOID BrdgProtRequestComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_REQUEST NdisRequest, IN NDIS_STATUS Status ) /*++ Routine Description: Completion handler for the previously posted request. Arguments: ProtocolBindingContext Pointer to the adapter structure NdisRequest The posted request (this should actually be a pointer to an NDIS_REQUEST_BETTER structure) Status Completion status Return Value: None --*/ { PNDIS_REQUEST_BETTER pRequest = (PNDIS_REQUEST_BETTER)NdisRequest; // Communicate final status to blocked caller pRequest->Status = Status; // // Call the completion function if there is one. // Having a completion function and blocking against the // event are mutually exclusive, not least because the // completion function may free the memory block // holding the event. // if( pRequest->pFunc != NULL ) { (*pRequest->pFunc)(pRequest, pRequest->FuncArg); } else { NdisSetEvent( &pRequest->Event ); } } NDIS_STATUS BrdgProtDoRequest( NDIS_HANDLE BindingHandle, BOOLEAN bSet, NDIS_OID Oid, PVOID pBuffer, UINT BufferSize ) /*++ Routine Description: Calls NdisRequest to retrieve or set information from an underlying NIC, and blocks until the call completes. Must be called at PASSIVE level because we wait on an event. Arguments: BindingHandle Handle to the NIC bSet TRUE == set info, FALSE == query info Oid Request code pBuffer Output buffer BufferSize Size of output buffer Return Value: Status of the request --*/ { NDIS_STATUS Status; NDIS_REQUEST_BETTER Request; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); Request.Request.RequestType = bSet ? NdisRequestSetInformation : NdisRequestQueryInformation; Request.Request.DATA.QUERY_INFORMATION.Oid = Oid ; Request.Request.DATA.QUERY_INFORMATION.InformationBuffer = pBuffer; Request.Request.DATA.QUERY_INFORMATION.InformationBufferLength = BufferSize; NdisInitializeEvent( &Request.Event ); NdisResetEvent( &Request.Event ); Request.pFunc = NULL; Request.FuncArg = NULL; NdisRequest( &Status, BindingHandle, &Request.Request); if ( Status == NDIS_STATUS_PENDING ) { NdisWaitEvent( &Request.Event, 0 /*Wait forever*/ ); Status = Request.Status; } return Status; } VOID BrdgProtCleanup() /*++ Routine Description: Called during driver unload to do an orderly shutdown This function is guaranteed to be called exactly once Arguments: None Return Value: None --*/ { NDIS_STATUS NdisStatus; // Deregister ourselves as a protocol. This will cause calls to BrdgProtUnbindAdapter // for all open adapters. if (gProtHandle != NULL) { NDIS_HANDLE TmpHandle = gProtHandle; gProtHandle = NULL; NdisDeregisterProtocol(&NdisStatus, TmpHandle); SAFEASSERT( NdisStatus == NDIS_STATUS_SUCCESS ); } } // =========================================================================== // // PRIVATE FUNCTIONS // // =========================================================================== VOID BrdgProtUnload(VOID) { SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); BrdgShutdown(); } NDIS_STATUS BrdgProtCompleteBindAdapter( IN PADAPT pAdapt ) /*++ Routine Description: Called by BrdgProtOpenAdapterComplete to complete the process of binding to an underlying NIC Must be called at < DISPATCH_LEVEL because we call BrdgMiniInstantiateMiniport(). Arguments: pAdapt The adapter to finish setting up Return Value: Status of the operation. If the return code is != NDIS_STATUS_SUCCESS, the binding is aborted and this adapter is not used again. Any error must be logged since it causes us to fail to bind to an adapter. --*/ { NDIS_STATUS Status; LOCK_STATE LockState; SAFEASSERT(CURRENT_IRQL < DISPATCH_LEVEL); // // Query the adapter's friendly name. // Status = NdisQueryAdapterInstanceName(&pAdapt->DeviceDesc, pAdapt->BindingHandle); if( Status != NDIS_STATUS_SUCCESS ) { // We failed. BrdgProtLogAdapterError( EVENT_BRIDGE_ADAPTER_NAME_QUERY_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("Failed to get an adapter's friendly name: %08x\n", Status)); return Status; } // // Get the adapter's media state (connected / disconnected) // Status = BrdgProtDoRequest( pAdapt->BindingHandle, FALSE/*Query*/, OID_GEN_MEDIA_CONNECT_STATUS, &pAdapt->MediaState, sizeof(pAdapt->MediaState) ); if( Status != NDIS_STATUS_SUCCESS ) { // Some old crummy drivers don't support this OID pAdapt->MediaState = NdisMediaStateConnected; } // // Get the adapter's link speed // Status = BrdgProtDoRequest( pAdapt->BindingHandle, FALSE/*Query*/, OID_GEN_LINK_SPEED, &pAdapt->LinkSpeed, sizeof(pAdapt->LinkSpeed) ); if( Status != NDIS_STATUS_SUCCESS ) { BrdgProtLogAdapterErrorFriendly( EVENT_BRIDGE_ADAPTER_LINK_SPEED_QUERY_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("Couldn't get an adapter's link speed: %08x\n", Status)); return Status; } // // Get the adapter's MAC address // Status = BrdgProtDoRequest( pAdapt->BindingHandle, FALSE/*Query*/, OID_802_3_PERMANENT_ADDRESS, &pAdapt->MACAddr, sizeof(pAdapt->MACAddr) ); if( Status != NDIS_STATUS_SUCCESS ) { BrdgProtLogAdapterErrorFriendly( EVENT_BRIDGE_ADAPTER_MAC_ADDR_QUERY_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("Couldn't get an adapter's MAC address: %08x\n", Status)); return Status; } // // Get the adapter's physical medium // Status = BrdgProtDoRequest( pAdapt->BindingHandle, FALSE/*Query*/, OID_GEN_PHYSICAL_MEDIUM, &pAdapt->PhysicalMedium, sizeof(pAdapt->PhysicalMedium) ); if( Status != NDIS_STATUS_SUCCESS ) { // Most drivers don't actually support OID_GEN_PHYSICAL_MEDIUM yet. Fall back on // NO_MEDIUM when the driver can't report anything. pAdapt->PhysicalMedium = BRIDGE_NO_MEDIUM; } // // Give the miniport section a look at this adapter so it can set its MAC address // BrdgMiniInitFromAdapter( pAdapt ); // // If pAdapt->bCompatibilityMode is already TRUE, it means that we found a reg // key during the initial bind phase that forces this adapter to compatibility mode // or that we force all adapters into compatibility mode. // if( !pAdapt->bCompatibilityMode ) { ULONG Filter = NDIS_PACKET_TYPE_PROMISCUOUS; // Attempt to put the adapter into promiscuous receive mode. If it fails this OID, // we put the adapter into compatibility mode if( BrdgProtDoRequest( pAdapt->BindingHandle, TRUE/*Set*/, OID_GEN_CURRENT_PACKET_FILTER, &Filter, sizeof(Filter) ) != NDIS_STATUS_SUCCESS ) { // The adapter doesn't seem to be able to do promiscuous mode. Put it in // compatibility mode. DBGPRINT(PROT, ("Adapter %p failed to go promiscuous; putting it in COMPATIBILITY MODE\n", pAdapt)); pAdapt->bCompatibilityMode = TRUE; } else { // Set the filter back to nothing for now Filter = 0L; BrdgProtDoRequest( pAdapt->BindingHandle, TRUE/*Set*/, OID_GEN_CURRENT_PACKET_FILTER, &Filter, sizeof(Filter) ); } } // If the STA isn't active, make this adapter live now. if( gDisableSTA ) { pAdapt->State = Forwarding; // Put the adapter into its initial state BrdgProtDoAdapterStateChange( pAdapt ); } // Else we initialize the adapter's STA functions below // // Link the adapter into the queue // NdisAcquireReadWriteLock( &gAdapterListLock, TRUE /* Write access */, &LockState ); pAdapt->Next = gAdapterList; gAdapterList = pAdapt; gNumAdapters++; // Must update this inside the write lock on the adapter list if( pAdapt->bCompatibilityMode ) { gCompatAdaptersExist = TRUE; } NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); if (g_fIsTcpIpLoaded == TRUE) { // Inform the 1394 miniport that tcpip has been loaded BrdgSetMiniportsToBridgeMode(pAdapt, TRUE); } if( ! gDisableSTA ) { // // Let the STA section initialize this adapter. This has to be done after the adapter // has been linked into the global list. // BrdgSTAInitializeAdapter( pAdapt ); } // Tell the draining threads to take notice of the new adapter BrdgProtSignalAdapterListChange(); // Update the miniport's idea of our virtual media state and link speed BrdgMiniUpdateCharacteristics( TRUE /*Is a connectivity change*/ ); // Tell user-mode code about the new adapter BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyAddAdapter ); // If we haven't yet called the miniport's InstantiateMiniport() function, do it now, since we have // at least one adapter in the list if( InterlockedCompareExchange(&gHaveInitedMiniport, 1L, 0L) == 0L ) { // Miniport wasn't previously initialized BrdgMiniInstantiateMiniport(); } // We're all done, so let people use the adapter pAdapt->bResetting = FALSE; DBGPRINT(PROT, ("BOUND SUCCESSFULLY to adapter %ws\n", pAdapt->DeviceDesc.Buffer)); return NDIS_STATUS_SUCCESS; } VOID BrdgProtBindAdapter( OUT PNDIS_STATUS Status, IN NDIS_HANDLE BindContext, IN PNDIS_STRING DeviceName, IN PVOID SystemSpecific1, IN PVOID SystemSpecific2 ) /*++ Routine Description: Called by NDIS to bind to a miniport below. Must be called at PASSIVE_LEVEL because we call NdisOpenAdapter(). Arguments: Status - Return status of bind here. BindContext - Can be passed to NdisCompleteBindAdapter if this call is pended. DeviceName - Device name to bind to. This is passed to NdisOpenAdapter. SystemSpecific1 - Can be passed to NdisOpenProtocolConfiguration to read per-binding information SystemSpecific2 - Unused for NDIS 5.0. Return Value: NDIS_STATUS_PENDING if this call is pended. In this case call NdisCompleteBindAdapter to complete. Anything else completes this call synchronously --*/ { PADAPT pAdapt = NULL; NDIS_STATUS Sts; UINT MediumIndex; LONG AdaptSize; NDIS_HANDLE ConfigHandle; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); // Don't do any new binds if we're shutting down if( gShuttingDown ) { DBGPRINT(PROT, ("REFUSING to bind to new adapter during shutdown!\n")); *Status = NDIS_STATUS_NOT_ACCEPTED; return; } // Make sure we're not being asked to bind to ourselves! if( BrdgMiniIsBridgeDeviceName(DeviceName) ) { DBGPRINT(PROT, ("REFUSING to bind to SELF!\n")); *Status = NDIS_STATUS_NOT_ACCEPTED; return; } // // Allocate memory for the Adapter structure. // AdaptSize = sizeof(ADAPT) + DeviceName->MaximumLength; NdisAllocateMemoryWithTag(&pAdapt, AdaptSize, 'gdrB'); if (pAdapt == NULL) { *Status = NDIS_STATUS_RESOURCES; return; } // // Initialize the adapter structure // NdisZeroMemory(pAdapt, AdaptSize); pAdapt->AdaptSize = AdaptSize; pAdapt->DeviceName.Buffer = (WCHAR *)((PUCHAR)pAdapt + sizeof(ADAPT)); pAdapt->DeviceName.MaximumLength = DeviceName->MaximumLength; pAdapt->DeviceName.Length = DeviceName->Length; NdisMoveMemory(pAdapt->DeviceName.Buffer, DeviceName->Buffer, DeviceName->Length); NdisInitializeEvent( &pAdapt->Event ); NdisResetEvent( &pAdapt->Event ); NdisAllocateSpinLock( &pAdapt->QueueLock ); BrdgInitializeSingleList( &pAdapt->Queue ); pAdapt->bServiceInProgress = FALSE; pAdapt->bSTAInited = FALSE; // Start out with this TRUE so no one can use the adapter until we're done // initializing it pAdapt->bResetting = TRUE; // Zero out statistics pAdapt->SentFrames.LowPart = pAdapt->SentFrames.HighPart = 0L; pAdapt->SentBytes.LowPart = pAdapt->SentBytes.HighPart = 0L; pAdapt->SentLocalFrames.LowPart = pAdapt->SentLocalFrames.HighPart = 0L; pAdapt->SentLocalBytes.LowPart = pAdapt->SentLocalBytes.HighPart = 0L; pAdapt->ReceivedFrames.LowPart = pAdapt->ReceivedFrames.HighPart = 0L; pAdapt->ReceivedBytes.LowPart = pAdapt->ReceivedBytes.HighPart = 0L; // The adapter starts off disabled pAdapt->State = Disabled; // Initialize quota information BrdgBufInitializeQuota( &pAdapt->Quota ); BrdgInitializeWaitRef( &pAdapt->Refcount, FALSE ); BrdgInitializeWaitRef( &pAdapt->QueueRefcount, FALSE ); KeInitializeEvent( &pAdapt->QueueEvent, SynchronizationEvent, FALSE ); pAdapt->bCompatibilityMode = FALSE; #if DBG if( gAllAdaptersCompat ) { pAdapt->bCompatibilityMode = TRUE; } else { #endif // Check if a registry entry forces this adapter to compatibility mode NdisOpenProtocolConfiguration( Status, &ConfigHandle, SystemSpecific1); if( *Status == NDIS_STATUS_SUCCESS ) { PNDIS_CONFIGURATION_PARAMETER pncp; NdisReadConfiguration( Status, &pncp, ConfigHandle, &gForceCompatValueName, NdisParameterHexInteger ); if( (*Status == NDIS_STATUS_SUCCESS) && (pncp->ParameterData.IntegerData != 0L ) ) { DBGPRINT(PROT, ("Forcing adapter into COMPATIBILITY MODE as per registry entry\n")); pAdapt->bCompatibilityMode = TRUE; } NdisCloseConfiguration( ConfigHandle ); } else { DBGPRINT(PROT, ("Failed to open protocol configuration for an adapter: %8x\n", *Status)); } #if DBG } #endif // // Now open the adapter below // NdisOpenAdapter(Status, &Sts, &pAdapt->BindingHandle, &MediumIndex, gMediumArray, sizeof(gMediumArray)/sizeof(NDIS_MEDIUM), gProtHandle, pAdapt, DeviceName, 0, NULL); if ( *Status == NDIS_STATUS_PENDING ) { // The bind will complete later in BrdgProtOpenAdapterComplete } else { // Complete the bind right away BrdgProtOpenAdapterComplete( (NDIS_HANDLE)pAdapt, *Status, *Status ); } } VOID BrdgProtOpenAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus ) /*++ Routine Description: Completion routine for NdisOpenAdapter issued from within the BrdgProtBindAdapter. Simply unblock the caller. Must be called at PASSIVE_LEVEL because we wait on an event. Arguments: ProtocolBindingContext Pointer to the adapter Status Status of the NdisOpenAdapter call OpenErrorStatus Secondary status(ignored by us). Return Value: None --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); if( Status != NDIS_STATUS_SUCCESS ) { // Log this error since it means we can't use the adapter. BrdgProtLogAdapterError( EVENT_BRIDGE_ADAPTER_BIND_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("BIND FAILURE: Failed to open adapter: %08x, %08x\n", Status, OpenErrorStatus)); NdisFreeMemory( pAdapt, pAdapt->AdaptSize, 0 ); } else { // BrdgProtCompleteBindAdapter must log any fatal errors Status = BrdgProtCompleteBindAdapter( pAdapt ); if( Status != NDIS_STATUS_SUCCESS ) { DBGPRINT(PROT, ("BIND FAILURE: Couldn't complete adapter initialization: %08x\n", Status)); BrdgSetMiniportsToBridgeMode (pAdapt, FALSE); // Turn bridge mode off on pAdapt NdisCloseAdapter( &Status, pAdapt->BindingHandle ); if ( Status == NDIS_STATUS_PENDING ) { NdisWaitEvent( &pAdapt->Event, 0/*Wait forever*/ ); } NdisFreeMemory( pAdapt, pAdapt->AdaptSize, 0 ); } } } VOID BrdgProtUnbindAdapter( OUT PNDIS_STATUS pStatus, IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE UnbindContext ) /*++ Routine Description: Called by NDIS when we are required to unbind to the adapter below. Must be called at PASSIVE_LEVEL because we wait on an event Arguments: pStatus Placeholder for return status ProtocolBindingContext Pointer to the adapter structure UnbindContext Context for NdisUnbindComplete() if this pends Return Value: None --*/ { PADAPT *pTmp, pAnAdapt, pAdapt =(PADAPT)ProtocolBindingContext; LOCK_STATE LockState; ULONG Filter; BOOLEAN bFound = FALSE, bCompatAdaptersExist; SAFEASSERT(CURRENT_IRQL == PASSIVE_LEVEL); DBGPRINT(PROT, ("UNBINDING Adapter %p :\n", pAdapt)); DBGPRINT(PROT, ("%ws\n", pAdapt->DeviceDesc.Buffer)); // Set the Underlying miniports to Off BrdgSetMiniportsToBridgeMode(pAdapt,FALSE); // Shut off all packet reception as the first order of business Filter = 0L; BrdgProtDoRequest( pAdapt->BindingHandle, TRUE/*Set*/, OID_GEN_CURRENT_PACKET_FILTER, &Filter, sizeof(Filter) ); // Take this adapter out of the queue NdisAcquireReadWriteLock( &gAdapterListLock, TRUE /* Write access */, &LockState ); for (pTmp = &gAdapterList; *pTmp != NULL; pTmp = &(*pTmp)->Next) { if (*pTmp == pAdapt) { *pTmp = pAdapt->Next; bFound = TRUE; break; } } gNumAdapters--; SAFEASSERT ( bFound ); // Find out if there are any compat-mode adapters left bCompatAdaptersExist = FALSE; for( pAnAdapt = gAdapterList; pAnAdapt != NULL; pAnAdapt = pAnAdapt->Next) { if( pAnAdapt->bCompatibilityMode ) { bCompatAdaptersExist = TRUE; } } // Must update this inside the write lock gCompatAdaptersExist = bCompatAdaptersExist; NdisReleaseReadWriteLock( &gAdapterListLock, &LockState ); // // Now no code will attempt to target this adapter for floods. // // Scrub this adapter from our tables so no one will attempt to target it. BrdgProtScrubAdapter( pAdapt ); // Stop packet forwarding on this adapter if( gDisableSTA ) { pAdapt->State = Disabled; } else { // Have the STA shut down its operations on this adapter BrdgSTAShutdownAdapter( pAdapt ); } // // Prevent new packets from being processed on this adapter // BrdgBlockWaitRef( &pAdapt->Refcount ); // // Wait for this adapter's queue to be drained by the worker threads. // BrdgShutdownWaitRefOnce( &pAdapt->QueueRefcount ); // // Signal the change in adapter list to the queue-draining threads. // This will remove this adapter from the threads' list of queues they block against. // BrdgProtSignalAdapterListChange(); // // Must wait for adapter refcount to go to zero before closing down the adapter. // This doesn't mean all requests have completed, just that none of our code is // holding this adapter's pointer anymore. // // Our receive functions bump up the refcount while they're processing an // inbound packet, so when the refcount drops to zero we should also have completed // any in-progress handling of received packets. // // The queue-draining threads also increment the refcount for adapters they are // using, so this wait is our guarantee that all threads have stopped using this // adapter as well. // BrdgShutdownWaitRefOnce( &pAdapt->Refcount ); SAFEASSERT( pAdapt->Refcount.Refcount == 0L ); // // Close this binding. This will pend till all NDIS requests in progress are // completed. // NdisResetEvent( &pAdapt->Event ); NdisCloseAdapter( pStatus, pAdapt->BindingHandle ); if ( *pStatus == NDIS_STATUS_PENDING ) { NdisWaitEvent( &pAdapt->Event, 0 /*Wait forever*/ ); } // Tell user-mode code the adapter left (this call should not attempt to read from // pAdapt) BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyRemoveAdapter ); // Free adapter resources if (pAdapt->DeviceDesc.Buffer != NULL) { NdisFreeMemory(pAdapt->DeviceDesc.Buffer, pAdapt->DeviceDesc.MaximumLength, 0); } NdisFreeMemory(pAdapt, pAdapt->AdaptSize, 0); DBGPRINT(PROT, ("Unbind complete.\n")); // Have the miniport update in light of the missing adapter BrdgMiniUpdateCharacteristics( TRUE /*Is a connectivity change*/ ); *pStatus = NDIS_STATUS_SUCCESS; } VOID BrdgProtDoAdapterStateChange( IN PADAPT pAdapt ) /*++ Routine Description: Adjusts an adapter's packet filter and multicast list based on its current state. If the adapter is Forwarding or Learning, the adapter is put in promiscuous mode so all packets are received. If the adapter is Blocking or Listening, the adapter is set to receive only the STA multicast packets. Errors are logged since this is a vital operation Arguments: pAdapt The adapter Return Value: Status code of the operation --*/ { NDIS_STATUS Status = NDIS_STATUS_FAILURE; ULONG Filter; PORT_STATE State = pAdapt->State; // Freeze this value BOOLEAN bReceiveAllMode = (BOOLEAN)((State == Forwarding) || (State == Learning)); if( ! bReceiveAllMode ) { // // Even if we're not forwarding packets off this interface, we still need to listen // for Spanning Tree Algorithm traffic // Status = BrdgProtDoRequest( pAdapt->BindingHandle, TRUE/*Set*/, OID_802_3_MULTICAST_LIST, STA_MAC_ADDR, sizeof(STA_MAC_ADDR) ); if( Status != NDIS_STATUS_SUCCESS ) { BrdgProtLogAdapterErrorFriendly( EVENT_BRIDGE_ADAPTER_FILTER_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("Failed to set adapter %p's multicast list: %08x\n", pAdapt, Status)); return; } } // Now set the packet filter appropriately if( pAdapt->bCompatibilityMode ) { // // Compatibility adapters can't do promiscuous properly. Our compatibility // code relies only on them receiving traffic unicast to this machine, as // well as all broadcast and multicast traffic. // Filter = bReceiveAllMode ? NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST : NDIS_PACKET_TYPE_MULTICAST; } else { Filter = bReceiveAllMode ? NDIS_PACKET_TYPE_PROMISCUOUS : NDIS_PACKET_TYPE_MULTICAST; } Status = BrdgProtDoRequest( pAdapt->BindingHandle, TRUE/*Set*/, OID_GEN_CURRENT_PACKET_FILTER, &Filter, sizeof(Filter) ); if( Status != NDIS_STATUS_SUCCESS ) { BrdgProtLogAdapterErrorFriendly( EVENT_BRIDGE_ADAPTER_FILTER_FAILED, pAdapt, Status ); DBGPRINT(PROT, ("Failed to set adapter %p's packet filter: %08x\n", pAdapt, Status)); } // Tell the miniport about the change so it can change the bridge's characteristics if it wants. BrdgMiniUpdateCharacteristics( FALSE /*Not a physical connectivity change*/ ); } VOID BrdgProtCloseAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status ) /*++ Routine Description: Completion for the CloseAdapter call. Just unblocks waiting code Arguments: ProtocolBindingContext Pointer to the adapter structure Status Completion status Return Value: None. --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; NdisSetEvent( &pAdapt->Event ); } VOID BrdgProtReceiveComplete( IN NDIS_HANDLE ProtocolBindingContext ) /*++ Routine Description: Called by the adapter below us when it is done indicating a batch of received buffers. Arguments: ProtocolBindingContext Pointer to our adapter structure. Return Value: None --*/ { // // Nothing to do here // } VOID BrdgProtStatus( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID StatusBuffer, IN UINT StatusBufferSize ) /*++ Routine Description: Handles status indications from underlying adapters Arguments: ProtocolBindingContext Pointer to the adapter structure GeneralStatus Status code StatusBuffer Status buffer StatusBufferSize Size of the status buffer Return Value: None --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; switch( GeneralStatus ) { case NDIS_STATUS_MEDIA_DISCONNECT: case NDIS_STATUS_MEDIA_CONNECT: { if( pAdapt != NULL ) { LOCK_STATE LockState; ULONG MediaState = (GeneralStatus == NDIS_STATUS_MEDIA_CONNECT) ? NdisMediaStateConnected : NdisMediaStateDisconnected; if( GeneralStatus == NDIS_STATUS_MEDIA_DISCONNECT ) { // Scrub the disconnected adapter from our tables. We will // have to relearn its hosts BrdgProtScrubAdapter( pAdapt ); } if( ! gDisableSTA ) { // The STA needs to know when adapters connect and disconnect if( MediaState == NdisMediaStateConnected ) { BrdgSTAEnableAdapter( pAdapt ); } else { BrdgSTADisableAdapter( pAdapt ); } } // A global lock is used for adapter characteristics, since they must be read // all at once by the miniport NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, TRUE /*Write access*/, &LockState ); pAdapt->MediaState = MediaState; NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &LockState ); // See if this makes any difference to our overall state BrdgMiniUpdateCharacteristics( TRUE /*Is a connectivity change*/ ); // Tell user-mode code that the adapter media state changed BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyMediaStateChange ); } else { DBGPRINT(PROT, ("BrdgProtStatus called for link status with NULL adapter!\n")); } } break; case NDIS_STATUS_LINK_SPEED_CHANGE: { if( (pAdapt != NULL) && (StatusBuffer != NULL) && (StatusBufferSize >= sizeof(ULONG)) ) { LOCK_STATE LockState; // A global lock is used for adapter characteristics, since they must be read // all at once by the miniport NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, TRUE /*Write access*/, &LockState ); pAdapt->LinkSpeed = *((ULONG*)StatusBuffer); NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &LockState ); if( ! gDisableSTA ) { // Tell the STA about the change so it can tweak the cost of this link BrdgSTAUpdateAdapterCost( pAdapt, *((ULONG*)StatusBuffer) ); } // See if this makes any difference to our overall state BrdgMiniUpdateCharacteristics( FALSE /*Not a connectivity change*/ ); // Tell user-mode code that the adapter speed changed BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyLinkSpeedChange ); } else { DBGPRINT(PROT, ("BrdgProtStatus called for link speed with bad params!\n")); } } break; case NDIS_STATUS_RESET_START: { DBGPRINT(PROT, ("Adapter %p RESET START\n", pAdapt)); pAdapt->bResetting = TRUE; } break; case NDIS_STATUS_RESET_END: { DBGPRINT(PROT, ("Adapter %p RESET END\n", pAdapt)); pAdapt->bResetting = FALSE; } break; default: { DBGPRINT(PROT, ("Unhandled status indication: %08x\n", GeneralStatus)); } break; } } VOID BrdgProtStatusComplete( IN NDIS_HANDLE ProtocolBindingContext ) /*++ Routine Description: NDIS entry point called when a status indication completes. We do nothing in response to this. Arguments: ProtocolBindingContext The adapter involved Return Value: None --*/ { // // Nothing to do here // } VOID BrdgProtInstantiateMiniport( IN PVOID unused ) /*++ Routine Description: Deferrable function to call BrdgMiniInstantiateMiniport(), which must run at low IRQL Must run at < DISPATCH_LEVEL Arguments: unused Unused Return Value: None --*/ { SAFEASSERT(CURRENT_IRQL < DISPATCH_LEVEL); BrdgMiniInstantiateMiniport(); } NDIS_STATUS BrdgProtPnPEvent( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_PNP_EVENT NetPnPEvent ) /*++ Routine Description: NDIS entry point called to indicate a PnP event to us Arguments: ProtocolBindingContext The adapter involved NetPnPEvent The event Return Value: Our status code in response to the event (should be NDIS_STATUS_SUCCESS or NDIS_STATUS_UNSUPPORTED) --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; switch( NetPnPEvent->NetEvent ) { case NetEventBindsComplete: case NetEventSetPower: case NetEventQueryPower: case NetEventCancelRemoveDevice: case NetEventBindList: case NetEventQueryRemoveDevice: case NetEventPnPCapabilities: { return NDIS_STATUS_SUCCESS; } break; case NetEventReconfigure: { if( pAdapt == NULL ) { NDIS_HANDLE MiniportHandle; // // A NetEventReconfigure event with a NULL binding context is either a // global indication of config changes or a signal from NDIS to restart // our miniport (for example, if it got disabled and then re-enabled). // // We're only interested in the case of a signal to restart our miniport. // We'll assume this can only happen after we have initialized it the // first time around. // // This is wierd, I know. // MiniportHandle = BrdgMiniAcquireMiniport(); if( gHaveInitedMiniport && (MiniportHandle == NULL) ) { // Our miniport isn't initialized. Fire it up again. // We can't do this at the high IRQL we're currently at, so defer the function. DBGPRINT(PROT, ("Restarting miniport in response to NULL NetEventReconfigure signal\n")); BrdgDeferFunction( BrdgProtInstantiateMiniport, NULL ); } if( MiniportHandle != NULL ) { BrdgMiniReleaseMiniport(); } } return NDIS_STATUS_SUCCESS; } break; } DBGPRINT(PROT, ("Unsupported PnP Code: %i\n", NetPnPEvent->NetEvent)); return NDIS_STATUS_NOT_SUPPORTED; } UINT BrdgProtCoReceive( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET Packet ) /*++ Routine Description: NDIS entry point called to indicate packets that are being sent on the Co-Ndis path. Arguments: Return Value: Return 0 as this is a do-nothing function --*/ { return 0; }