/*++ Copyright(c) 1992-2000 Microsoft Corporation Module Name: protocol.c Abstract: NDIS Protocol Entry points and utility functions for the NDIS MUX Intermediate Miniport sample. The protocol edge binds to Ethernet (NdisMedium802_3) adapters, and initiates creation of zero or more Virtual Ethernet LAN (VELAN) miniport instances by calling NdisIMInitializeDeviceInstanceEx once for each VELAN configured over a lower binding. Environment: Kernel mode. Revision History: --*/ #include "precomp.h" #pragma hdrstop #define MODULE_NUMBER MODULE_PROT VOID PtBindAdapter( 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. This routine creates a binding by calling NdisOpenAdapter, and then initiates creation of all configured VELANs on this binding. 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 Return Value: *Status is set to NDIS_STATUS_SUCCESS if no failure occurred while handling this call, otherwise an error code. --*/ { PADAPT pAdapt = NULL; NDIS_STATUS OpenErrorStatus; UINT MediumIndex; PNDIS_STRING pConfigString; ULONG Length; UNREFERENCED_PARAMETER(BindContext); UNREFERENCED_PARAMETER(SystemSpecific2); pConfigString = (PNDIS_STRING)SystemSpecific1; DBGPRINT(MUX_LOUD, ("==> Protocol BindAdapter: %ws\n", pConfigString->Buffer)); do { // // Allocate memory for Adapter struct plus the config // string with two extra WCHARs for NULL termination. // Length = sizeof(ADAPT) + pConfigString->MaximumLength + sizeof(WCHAR); NdisAllocateMemoryWithTag(&pAdapt, Length , TAG); if (pAdapt == NULL) { *Status = NDIS_STATUS_RESOURCES; break; } // // Initialize the adapter structure // NdisZeroMemory(pAdapt, sizeof(ADAPT)); (VOID)PtReferenceAdapter(pAdapt, (PUCHAR)"openadapter"); // // Copy in the Config string - we will use this to open the // registry section for this adapter at a later point. // pAdapt->ConfigString.MaximumLength = pConfigString->MaximumLength; pAdapt->ConfigString.Length = pConfigString->Length; pAdapt->ConfigString.Buffer = (PWCHAR)((PUCHAR)pAdapt + sizeof(ADAPT)); NdisMoveMemory(pAdapt->ConfigString.Buffer, pConfigString->Buffer, pConfigString->Length); pAdapt->ConfigString.Buffer[pConfigString->Length/sizeof(WCHAR)] = ((WCHAR)0); NdisInitializeEvent(&pAdapt->Event); NdisInitializeListHead(&pAdapt->VElanList); pAdapt->PtDevicePowerState = NdisDeviceStateD0; MUX_INIT_ADAPT_RW_LOCK(pAdapt); // // TODO: Allocate a packet pool and buffers for send & receive. // // Now open the adapter below and complete the initialization // NdisOpenAdapter(Status, &OpenErrorStatus, &pAdapt->BindingHandle, &MediumIndex, MediumArray, sizeof(MediumArray)/sizeof(NDIS_MEDIUM), ProtHandle, pAdapt, DeviceName, 0, NULL); if (*Status == NDIS_STATUS_PENDING) { NdisWaitEvent(&pAdapt->Event, 0); *Status = pAdapt->Status; } if (*Status != NDIS_STATUS_SUCCESS) { pAdapt->BindingHandle = NULL; break; } pAdapt->Medium = MediumArray[MediumIndex]; // // Add this adapter to the global AdapterList // MUX_ACQUIRE_MUTEX(&GlobalMutex); InsertTailList(&AdapterList, &pAdapt->Link); MUX_RELEASE_MUTEX(&GlobalMutex); // // Get some information from the adapter below. // PtQueryAdapterInfo(pAdapt); // // Start all VELANS configured on this adapter. // *Status = PtBootStrapVElans(pAdapt); } while(FALSE); if (*Status != NDIS_STATUS_SUCCESS) { if (pAdapt != NULL) { // // For some reason, the driver cannot create velan for the binding // if (pAdapt->BindingHandle != NULL) { NDIS_STATUS LocalStatus; // // Close the binding the driver opened above // NdisResetEvent(&pAdapt->Event); NdisCloseAdapter(&LocalStatus, pAdapt->BindingHandle); pAdapt->BindingHandle = NULL; if (LocalStatus == NDIS_STATUS_PENDING) { NdisWaitEvent(&pAdapt->Event, 0); } MUX_ACQUIRE_MUTEX(&GlobalMutex); RemoveEntryList(&pAdapt->Link); MUX_RELEASE_MUTEX(&GlobalMutex); } PtDereferenceAdapter(pAdapt, (PUCHAR)"openadapter"); pAdapt = NULL; } } DBGPRINT(MUX_INFO, ("<== Protocol BindAdapter: pAdapt %p, Status %x\n", pAdapt, *Status)); } VOID PtOpenAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status, IN NDIS_STATUS OpenErrorStatus ) /*++ Routine Description: Completion routine for NdisOpenAdapter issued from within the PtBindAdapter. Simply unblock the caller. 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; UNREFERENCED_PARAMETER(OpenErrorStatus); DBGPRINT(MUX_LOUD, ("==> PtOpenAdapterComplete: Adapt %p, Status %x\n", pAdapt, Status)); pAdapt->Status = Status; NdisSetEvent(&pAdapt->Event); } VOID PtQueryAdapterInfo( IN PADAPT pAdapt ) /*++ Routine Description: Query the adapter we are bound to for some standard OID values which we cache. Arguments: pAdapt Pointer to the adapter Return Value: None --*/ { // // Get the link speed. // pAdapt->LinkSpeed = MUX_DEFAULT_LINK_SPEED; PtQueryAdapterSync(pAdapt, OID_GEN_LINK_SPEED, &pAdapt->LinkSpeed, sizeof(pAdapt->LinkSpeed)); // // Get the max lookahead size. // pAdapt->MaxLookAhead = MUX_DEFAULT_LOOKAHEAD_SIZE; PtQueryAdapterSync(pAdapt, OID_GEN_MAXIMUM_LOOKAHEAD, &pAdapt->MaxLookAhead, sizeof(pAdapt->MaxLookAhead)); // // Get the Ethernet MAC address. // PtQueryAdapterSync(pAdapt, OID_802_3_CURRENT_ADDRESS, &pAdapt->CurrentAddress, sizeof(pAdapt->CurrentAddress)); } VOID PtQueryAdapterSync( IN PADAPT pAdapt, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength ) /*++ Routine Description: Utility routine to query the adapter for a single OID value. This blocks for the query to complete. Arguments: pAdapt Pointer to the adapter Oid OID to query for InformationBuffer Place for the result InformationBufferLength Length of the above Return Value: None. --*/ { PMUX_NDIS_REQUEST pMuxNdisRequest = NULL; NDIS_STATUS Status; do { NdisAllocateMemoryWithTag(&pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), TAG); if (pMuxNdisRequest == NULL) { break; } pMuxNdisRequest->pVElan = NULL; // internal request // // Set up completion routine. // pMuxNdisRequest->pCallback = PtCompleteBlockingRequest; NdisInitializeEvent(&pMuxNdisRequest->Event); pMuxNdisRequest->Request.RequestType = NdisRequestQueryInformation; pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid = Oid; pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer; pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength; NdisRequest(&Status, pAdapt->BindingHandle, &pMuxNdisRequest->Request); if (Status == NDIS_STATUS_PENDING) { NdisWaitEvent(&pMuxNdisRequest->Event, 0); Status = pMuxNdisRequest->Status; } } while (FALSE); if (NULL != pMuxNdisRequest) { NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0); } } VOID PtRequestAdapterAsync( IN PADAPT pAdapt, IN NDIS_REQUEST_TYPE RequestType, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, IN PMUX_REQ_COMPLETE_HANDLER pCallback ) /*++ Routine Description: Utility routine to query the adapter for a single OID value. This completes asynchronously, i.e. the calling thread is not blocked until the request completes. Arguments: pAdapt Pointer to the adapter RequestType NDIS request type Oid OID to set/query InformationBuffer Input/output buffer InformationBufferLength Length of the above pCallback Function to call on request completion Return Value: None. --*/ { PMUX_NDIS_REQUEST pMuxNdisRequest = NULL; PNDIS_REQUEST pNdisRequest; NDIS_STATUS Status; do { NdisAllocateMemoryWithTag(&pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), TAG); if (pMuxNdisRequest == NULL) { break; } pMuxNdisRequest->pVElan = NULL; // internal request // // Set up completion routine. // pMuxNdisRequest->pCallback = pCallback; pNdisRequest = &pMuxNdisRequest->Request; pNdisRequest->RequestType = RequestType; switch (RequestType) { case NdisRequestQueryInformation: pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer; pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength; break; case NdisRequestSetInformation: pNdisRequest->DATA.SET_INFORMATION.Oid = Oid; pNdisRequest->DATA.SET_INFORMATION.InformationBuffer = InformationBuffer; pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength; break; default: ASSERT(FALSE); break; } NdisRequest(&Status, pAdapt->BindingHandle, pNdisRequest); if (Status != NDIS_STATUS_PENDING) { PtRequestComplete( (NDIS_HANDLE)pAdapt, pNdisRequest, Status); } } while (FALSE); } VOID PtUnbindAdapter( OUT PNDIS_STATUS Status, IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE UnbindContext ) /*++ Routine Description: Called by NDIS when we are required to unbind to the adapter below. Go through all VELANs on the adapter and shut them down. Arguments: Status Placeholder for return status ProtocolBindingContext Pointer to the adapter structure UnbindContext Context for NdisUnbindComplete() if this pends Return Value: Status from closing the binding. --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; PLIST_ENTRY p; PVELAN pVElan = NULL; LOCK_STATE LockState; UNREFERENCED_PARAMETER(UnbindContext); DBGPRINT(MUX_LOUD, ("==> PtUnbindAdapter: Adapt %p\n", pAdapt)); // // Stop all VELANs associated with the adapter. // Repeatedly find the first unprocessed VELAN on // the adapter, mark it, and stop it. // MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); do { for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); if (!pVElan->DeInitializing) { pVElan->DeInitializing = TRUE; break; } } if (p != &pAdapt->VElanList) { ASSERT(pVElan == CONTAINING_RECORD(p, VELAN, Link)); // // Got a VELAN to stop. Add a temp ref // so that the VELAN won't go away when // we release the ADAPT lock below. // PtReferenceVElan(pVElan, (PUCHAR)"UnbindTemp"); // // Release the read lock because we want to // run StopVElan at passive IRQL. // MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); PtStopVElan(pVElan); PtDereferenceVElan(pVElan, (PUCHAR)"UnbindTemp"); MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); } else { // // No unmarked VELAN, so exit. // break; } } while (TRUE); // // Wait until all VELANs are unlinked from the adapter. // This is so that we don't attempt to forward down packets // and/or requests from VELANs after calling NdisCloseAdapter. // while (!IsListEmpty(&pAdapt->VElanList)) { MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); DBGPRINT(MUX_INFO, ("PtUnbindAdapter: pAdapt %p, VELANlist not yet empty\n", pAdapt)); NdisMSleep(2000); MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); // // Close the binding to the lower adapter. // if (pAdapt->BindingHandle != NULL) { NdisResetEvent(&pAdapt->Event); NdisCloseAdapter(Status, pAdapt->BindingHandle); // // Wait for it to complete. // if (*Status == NDIS_STATUS_PENDING) { NdisWaitEvent(&pAdapt->Event, 0); *Status = pAdapt->Status; } } else { // // Binding Handle should not be NULL. // *Status = NDIS_STATUS_FAILURE; ASSERT(0); } // // Remove the adapter from the global AdapterList // MUX_ACQUIRE_MUTEX(&GlobalMutex); RemoveEntryList(&pAdapt->Link); MUX_RELEASE_MUTEX(&GlobalMutex); // // Free all the resources associated with this Adapter except the // ADAPT struct itself, because that will be freed by // PtDereferenceAdapter call when the reference drops to zero. // Note: Every VELAN associated with this Adapter takes a ref count // on it. So the adapter memory wouldn't be freed until all the VELANs // are shutdown. // PtDereferenceAdapter(pAdapt, (PUCHAR)"Unbind"); DBGPRINT(MUX_INFO, ("<== PtUnbindAdapter: Adapt %p\n", pAdapt)); } VOID PtCloseAdapterComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status ) /*++ Routine Description: Completion for the CloseAdapter call. Arguments: ProtocolBindingContext Pointer to the adapter structure Status Completion status Return Value: None. --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; DBGPRINT(MUX_INFO, ("==> PtCloseAdapterComplete: Adapt %p, Status %x\n", pAdapt, Status)); pAdapt->Status = Status; NdisSetEvent(&pAdapt->Event); } VOID PtResetComplete( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS Status ) /*++ Routine Description: Completion for the reset. Arguments: ProtocolBindingContext Pointer to the adapter structure Status Completion status Return Value: None. --*/ { #if DBG PADAPT pAdapt =(PADAPT)ProtocolBindingContext; #endif #if !DBG UNREFERENCED_PARAMETER(ProtocolBindingContext); UNREFERENCED_PARAMETER(Status); #endif DBGPRINT(MUX_ERROR, ("==> PtResetComplete: Adapt %p, Status %x\n", pAdapt, Status)); // // We never issue a reset, so we should not be here. // ASSERT(0); } VOID PtRequestComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_REQUEST NdisRequest, IN NDIS_STATUS Status ) /*++ Routine Description: Completion handler for an NDIS request sent to a lower miniport. Arguments: ProtocolBindingContext Pointer to the adapter structure NdisRequest The completed request Status Completion status Return Value: None --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; PMUX_NDIS_REQUEST pMuxNdisRequest; pMuxNdisRequest = CONTAINING_RECORD(NdisRequest, MUX_NDIS_REQUEST, Request); ASSERT(pMuxNdisRequest->pCallback != NULL); // // Completion is handled by the callback routine: // (*pMuxNdisRequest->pCallback)(pAdapt, pMuxNdisRequest, Status); } VOID PtCompleteForwardedRequest( IN PADAPT pAdapt, IN PMUX_NDIS_REQUEST pMuxNdisRequest, IN NDIS_STATUS Status ) /*++ Routine Description: Handle completion of an NDIS request that was originally submitted to our VELAN miniport and was forwarded down to the lower binding. We do some postprocessing, to cache the results of certain queries. Arguments: pAdapt - Adapter on which the request was forwarded pMuxNdisRequest - super-struct for request Status - request completion status Return Value: None --*/ { PVELAN pVElan = NULL; PNDIS_REQUEST pNdisRequest = &pMuxNdisRequest->Request; NDIS_OID Oid = pNdisRequest->DATA.SET_INFORMATION.Oid; UNREFERENCED_PARAMETER(pAdapt); // // Get the originating VELAN. The VELAN will not be dereferenced // away until the pended request is completed. // pVElan = pMuxNdisRequest->pVElan; ASSERT(pVElan != NULL); ASSERT(pMuxNdisRequest == &pVElan->Request); if (Status != NDIS_STATUS_SUCCESS) { DBGPRINT(MUX_WARN, ("PtCompleteForwardedReq: pVElan %p, OID %x, Status %x\n", pVElan, pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid, Status)); } // // Complete the original request. // switch (pNdisRequest->RequestType) { case NdisRequestQueryInformation: *pVElan->BytesReadOrWritten = pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten; *pVElan->BytesNeeded = pNdisRequest->DATA.QUERY_INFORMATION.BytesNeeded; // // Before completing the request, do any necessary // post-processing. // Oid = pNdisRequest->DATA.QUERY_INFORMATION.Oid; if (Status == NDIS_STATUS_SUCCESS) { if (Oid == OID_GEN_LINK_SPEED) { NdisMoveMemory (&pVElan->LinkSpeed, pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, sizeof(ULONG)); } else if (Oid == OID_PNP_CAPABILITIES) { PtPostProcessPnPCapabilities(pVElan, pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength); } } NdisMQueryInformationComplete(pVElan->MiniportAdapterHandle, Status); break; case NdisRequestSetInformation: *pVElan->BytesReadOrWritten = pNdisRequest->DATA.SET_INFORMATION.BytesRead; *pVElan->BytesNeeded = pNdisRequest->DATA.SET_INFORMATION.BytesNeeded; // // Before completing the request, cache relevant information // in our structure. // if (Status == NDIS_STATUS_SUCCESS) { Oid = pNdisRequest->DATA.SET_INFORMATION.Oid; switch (Oid) { case OID_GEN_CURRENT_LOOKAHEAD: NdisMoveMemory(&pVElan->LookAhead, pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, sizeof(ULONG)); break; default: break; } } NdisMSetInformationComplete(pVElan->MiniportAdapterHandle, Status); break; default: ASSERT(FALSE); break; } MUX_DECR_PENDING_SENDS(pVElan); } VOID PtPostProcessPnPCapabilities( IN PVELAN pVElan, IN PVOID InformationBuffer, IN ULONG InformationBufferLength ) /*++ Routine Description: Postprocess a successfully completed query for OID_PNP_CAPABILITIES. We modify the returned information slightly before completing it to the VELAN above. Arguments: pVElan - Pointer to VELAN InformationBuffer - points to buffer for the OID InformationBufferLength - byte length of the above. Return Value: None --*/ { PNDIS_PNP_CAPABILITIES pPNPCapabilities; PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct; UNREFERENCED_PARAMETER(pVElan); if (InformationBufferLength >= sizeof(NDIS_PNP_CAPABILITIES)) { pPNPCapabilities = (PNDIS_PNP_CAPABILITIES)InformationBuffer; // // The following fields must be overwritten by an IM driver. // pPMstruct= &pPNPCapabilities->WakeUpCapabilities; pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified; pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified; pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified; } } VOID PtCompleteBlockingRequest( IN PADAPT pAdapt, IN PMUX_NDIS_REQUEST pMuxNdisRequest, IN NDIS_STATUS Status ) /*++ Routine Description: Handle completion of an NDIS request that was originated by this driver and the calling thread is blocked waiting for completion. Arguments: pAdapt - Adapter on which the request was forwarded pMuxNdisRequest - super-struct for request Status - request completion status Return Value: None --*/ { UNREFERENCED_PARAMETER(pAdapt); pMuxNdisRequest->Status = Status; // // The request was originated from this driver. Wake up the // thread blocked for its completion. // pMuxNdisRequest->Status = Status; NdisSetEvent(&pMuxNdisRequest->Event); } VOID PtDiscardCompletedRequest( IN PADAPT pAdapt, IN PMUX_NDIS_REQUEST pMuxNdisRequest, IN NDIS_STATUS Status ) /*++ Routine Description: Handle completion of an NDIS request that was originated by this driver - the request is to be discarded. Arguments: pAdapt - Adapter on which the request was forwarded pMuxNdisRequest - super-struct for request Status - request completion status Return Value: None --*/ { UNREFERENCED_PARAMETER(pAdapt); UNREFERENCED_PARAMETER(Status); NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0); } VOID PtStatus( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_STATUS GeneralStatus, IN PVOID StatusBuffer, IN UINT StatusBufferSize ) /*++ Routine Description: Handle a status indication on the lower binding (ADAPT). If this is a media status indication, we also pass this on to all associated VELANs. 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; PLIST_ENTRY p; PVELAN pVElan; LOCK_STATE LockState; DBGPRINT(MUX_LOUD, ("PtStatus: Adapt %p, Status %x\n", pAdapt, GeneralStatus)); do { // // Ignore status indications that we aren't going // to pass up. // if ((GeneralStatus != NDIS_STATUS_MEDIA_CONNECT) && (GeneralStatus != NDIS_STATUS_MEDIA_DISCONNECT)) { break; } MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); MUX_INCR_PENDING_RECEIVES(pVElan); // // Should the indication be sent on this VELAN? // if ((pVElan->MiniportHalting) || (pVElan->MiniportAdapterHandle == NULL) || MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) { MUX_DECR_PENDING_RECEIVES(pVElan); if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) { // // Keep track of the lastest status to indicated when VELAN power is on // ASSERT((GeneralStatus == NDIS_STATUS_MEDIA_CONNECT) || (GeneralStatus == NDIS_STATUS_MEDIA_DISCONNECT)); pVElan->LatestUnIndicateStatus = GeneralStatus; } continue; } // // Save the last indicated status when pVElan->LastIndicatedStatus = GeneralStatus; NdisMIndicateStatus(pVElan->MiniportAdapterHandle, GeneralStatus, StatusBuffer, StatusBufferSize); // // Mark this so that we forward a status complete // indication as well. // pVElan->IndicateStatusComplete = TRUE; MUX_DECR_PENDING_RECEIVES(pVElan); } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } while (FALSE); } VOID PtStatusComplete( IN NDIS_HANDLE ProtocolBindingContext ) /*++ Routine Description: Marks the end of a status indication. Pass it on to associated VELANs if necessary. Arguments: ProtocolBindingContext - pointer to ADAPT Return Value: None. --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; PLIST_ENTRY p; PVELAN pVElan; LOCK_STATE LockState; MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); MUX_INCR_PENDING_RECEIVES(pVElan); // // Should this indication be sent on this VELAN? // if ((pVElan->MiniportHalting) || (pVElan->MiniportAdapterHandle == NULL) || (!pVElan->IndicateStatusComplete) || (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))) { MUX_DECR_PENDING_RECEIVES(pVElan); continue; } pVElan->IndicateStatusComplete = FALSE; NdisMIndicateStatusComplete(pVElan->MiniportAdapterHandle); MUX_DECR_PENDING_RECEIVES(pVElan); } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } VOID PtSendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET Packet, IN NDIS_STATUS Status ) /*++ Routine Description: Called by NDIS when the miniport below had completed a send. We complete the corresponding upper-edge send this represents. The packet being completed belongs to our send packet pool, however we store a pointer to the original packet this represents, in the packet's reserved field. Arguments: ProtocolBindingContext - Points to ADAPT structure Packet - Packet being completed by the lower miniport Status - status of send Return Value: None --*/ { PVELAN pVElan; PMUX_SEND_RSVD pSendReserved; PNDIS_PACKET OriginalPacket; #if IEEE_VLAN_SUPPORT NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo; BOOLEAN IsTagInsert; PNDIS_BUFFER pNdisBuffer; PVOID pVa; ULONG BufferLength; #endif UNREFERENCED_PARAMETER(ProtocolBindingContext); pSendReserved = MUX_RSVD_FROM_SEND_PACKET(Packet); OriginalPacket = pSendReserved->pOriginalPacket; pVElan = pSendReserved->pVElan; #if IEEE_VLAN_SUPPORT // // Check if we had inserted a tag header // IsTagInsert = FALSE; NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET( OriginalPacket, Ieee8021QInfo); if ((pVElan->VlanId != 0) || (NdisPacket8021qInfo.Value != NULL)) { IsTagInsert = TRUE; } #endif #ifndef WIN9X NdisIMCopySendCompletePerPacketInfo(OriginalPacket, Packet); #endif // // Update statistics. // if (Status == NDIS_STATUS_SUCCESS) { MUX_INCR_STATISTICS64(&pVElan->GoodTransmits); } else { MUX_INCR_STATISTICS(&pVElan->TransmitFailuresOther); } // // Complete the original send. // NdisMSendComplete(pVElan->MiniportAdapterHandle, OriginalPacket, Status); #if IEEE_VLAN_SUPPORT // // If we had inserted a tag header, then remove the header // buffer and free it. We would also have created a new // NDIS buffer to map part of the original packet's header; // free that, too. // if (IsTagInsert) { pNdisBuffer = Packet->Private.Head; #ifdef NDIS51_MINIPORT NdisQueryBufferSafe(pNdisBuffer, &pVa, (PUINT)&BufferLength, NormalPagePriority); #else NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength); #endif if (pVa != NULL) { NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, pVa); } NdisFreeBuffer(NDIS_BUFFER_LINKAGE(pNdisBuffer)); NdisFreeBuffer (pNdisBuffer); } #endif // // Free our packet. // NdisFreePacket(Packet); // // Note down send-completion. // MUX_DECR_PENDING_SENDS(pVElan); } VOID PtTransferDataComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET Packet, IN NDIS_STATUS Status, IN UINT BytesTransferred ) /*++ Routine Description: Entry point called by NDIS to indicate completion of a call by us to NdisTransferData. We locate the original packet and VELAN on which our TransferData function (see MPTransferData) was called, and complete the original request. Arguments: ProtocolBindingContext - lower binding context, pointer to ADAPT Packet - Packet allocated by us Status - Completion status BytesTransferred - Number of bytes copied in Return Value: None --*/ { PVELAN pVElan; PNDIS_PACKET pOriginalPacket; PMUX_TD_RSVD pTDReserved; UNREFERENCED_PARAMETER(ProtocolBindingContext); pTDReserved = MUX_RSVD_FROM_TD_PACKET(Packet); pOriginalPacket = pTDReserved->pOriginalPacket; pVElan = pTDReserved->pVElan; // // Complete the original TransferData request. // NdisMTransferDataComplete(pVElan->MiniportAdapterHandle, pOriginalPacket, Status, BytesTransferred); // // Free our packet. // NdisFreePacket(Packet); } BOOLEAN PtMulticastMatch( IN PVELAN pVElan, IN PUCHAR pDstMac ) /*++ Routine Description: Check if the given multicast destination MAC address matches any of the multicast address entries set on the VELAN. NOTE: the caller is assumed to hold a READ/WRITE lock to the parent ADAPT structure. This is so that the multicast list on the VELAN is invariant for the duration of this call. Arguments: pVElan - VELAN to look in pDstMac - Destination MAC address to compare Return Value: TRUE iff the address matches an entry in the VELAN --*/ { ULONG i; UINT AddrCompareResult; for (i = 0; i < pVElan->McastAddrCount; i++) { ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->McastAddrs[i], pDstMac, &AddrCompareResult); if (AddrCompareResult == 0) { break; } } return (i != pVElan->McastAddrCount); } BOOLEAN PtMatchPacketToVElan( IN PVELAN pVElan, IN PUCHAR pDstMac, IN BOOLEAN bIsMulticast, IN BOOLEAN bIsBroadcast ) /*++ Routine Description: Check if the destination address of a received packet matches the receive criteria on the specified VELAN. NOTE: the caller is assumed to hold a READ/WRITE lock to the parent ADAPT structure. Arguments: pVElan - VELAN to check on pDstMac - Destination MAC address in received packet bIsMulticast - is this a multicast address bIsBroadcast - is this a broadcast address Return Value: TRUE iff this packet should be received on the VELAN --*/ { UINT AddrCompareResult; ULONG PacketFilter; BOOLEAN bPacketMatch; PacketFilter = pVElan->PacketFilter; // // Handle the directed packet case first. // if (!bIsMulticast) { // // If the VELAN is not in promisc. mode, check if // the destination MAC address matches the local // address. // if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == 0) { ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->CurrentAddress, pDstMac, &AddrCompareResult); bPacketMatch = ((AddrCompareResult == 0) && ((PacketFilter & NDIS_PACKET_TYPE_DIRECTED) != 0)); } else { bPacketMatch = TRUE; } } else { // // Multicast or broadcast packet. // // // Indicate if the filter is set to promisc mode ... // if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) || // // or if this is a broadcast packet and the filter // is set to receive all broadcast packets... // (bIsBroadcast && (PacketFilter & NDIS_PACKET_TYPE_BROADCAST)) || // // or if this is a multicast packet, and the filter is // either set to receive all multicast packets, or // set to receive specific multicast packets. In the // latter case, indicate receive only if the destn // MAC address is present in the list of multicast // addresses set on the VELAN. // (!bIsBroadcast && ((PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) || ((PacketFilter & NDIS_PACKET_TYPE_MULTICAST) && PtMulticastMatch(pVElan, pDstMac)))) ) { bPacketMatch = TRUE; } else { // // No protocols above are interested in this // multicast/broadcast packet. // bPacketMatch = FALSE; } } return (bPacketMatch); } NDIS_STATUS PtReceive( IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE MacReceiveContext, IN PVOID HeaderBuffer, IN UINT HeaderBufferSize, IN PVOID LookAheadBuffer, IN UINT LookAheadBufferSize, IN UINT PacketSize ) /*++ Routine Description: Handle receive data indicated up by the miniport below. We forward this up to all VELANs that are eligible to receive this packet: - If this is directed to a broadcast/multicast address, indicate up on all VELANs that have multicast or broadcast or promisc. bits set in their packet filters. - If this is a directed packet, indicate it up on all VELANs that have the a matching MAC address or have the promisc. bit set in their packet filters. We acquire a read lock on the ADAPT structure to ensure that the VELAN list on the adapter is undisturbed. If the miniport below indicates packets, NDIS would more likely call us at our ReceivePacket handler. However we might be called here in certain situations even though the miniport below has indicated a receive packet, e.g. if the miniport had set packet status to NDIS_STATUS_RESOURCES. Arguments: Return Value: NDIS_STATUS_SUCCESS if we processed the receive successfully, NDIS_STATUS_XXX error code if we discarded it. --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; PLIST_ENTRY p; PVELAN pVElan; PNDIS_PACKET MyPacket, Packet; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PUCHAR pData; PUCHAR pDstMac; BOOLEAN bIsMulticast, bIsBroadcast; PMUX_RECV_RSVD pRecvReserved; LOCK_STATE LockState; #if IEEE_VLAN_SUPPORT VLAN_TAG_HEADER UNALIGNED * pTagHeader; USHORT UNALIGNED * pTpid; MUX_RCV_CONTEXT MuxRcvContext; #endif do { if (HeaderBufferSize != ETH_HEADER_SIZE) { Status = NDIS_STATUS_NOT_ACCEPTED; break; } if (pAdapt->PacketFilter == 0) { // // We could get receives in the interval between // initiating a request to set the packet filter on // the binding to 0 and completion of that request. // Drop such packets. // Status = NDIS_STATUS_NOT_ACCEPTED; break; } // // Collect some information from the packet. // pData = (PUCHAR)HeaderBuffer; pDstMac = pData; bIsMulticast = ETH_IS_MULTICAST(pDstMac); bIsBroadcast = ETH_IS_BROADCAST(pDstMac); // // Get at the packet, if any, indicated up by the miniport below. // Packet = NdisGetReceivedPacket(pAdapt->BindingHandle, MacReceiveContext); // // Lock down the VELAN list on the adapter so that // no insertions/deletions to this list happen while // we loop through it. The packet filter will also not // change during the time we hold the read lock. // MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { BOOLEAN bIndicateReceive; pVElan = CONTAINING_RECORD(p, VELAN, Link); // // Should the packet be indicated up on this VELAN? // bIndicateReceive = PtMatchPacketToVElan(pVElan, pDstMac, bIsMulticast, bIsBroadcast); if (!bIndicateReceive) { continue; } // // Make sure we don't Halt the VELAN miniport while // we are accessing it here. See MPHalt. // // Also don't indicate receives if the virtual miniport // has been set to a low power state. A specific case // is when the system is resuming from "Stand-by", if // the lower adapter is restored to D0 before the upper // miniports are. // // MUX_INCR_PENDING_RECEIVES(pVElan); if ((pVElan->MiniportHalting) || (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))) { MUX_DECR_PENDING_RECEIVES(pVElan); continue; } if (Packet != NULL) { // // The miniport below did indicate up a packet. Use information // from that packet to construct a new packet to indicate up. // // // Get a packet off our receive pool and indicate that up. // NdisDprAllocatePacket(&Status, &MyPacket, pVElan->RecvPacketPoolHandle); if (Status == NDIS_STATUS_SUCCESS) { // // Make our packet point to data from the original // packet. NOTE: this works only because we are // indicating a receive directly from the context of // our receive indication. If we need to queue this // packet and indicate it from another thread context, // we will also have to allocate a new buffer and copy // over the packet contents, OOB data and per-packet // information. This is because the packet data // is available only for the duration of this // receive indication call. // MyPacket->Private.Head = Packet->Private.Head; MyPacket->Private.Tail = Packet->Private.Tail; #if IEEE_VLAN_SUPPORT Status = PtHandleRcvTagging(pVElan, Packet, MyPacket); if (Status != NDIS_STATUS_SUCCESS) { NdisFreePacket(MyPacket); MUX_DECR_PENDING_RECEIVES(pVElan); continue; } #endif // // Get the original packet (it could be the same packet // as the one received or a different one based on the // number of layered miniports below) and set it on the // indicated packet so the OOB data is visible correctly // at protocols above. // NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet)); NDIS_SET_PACKET_HEADER_SIZE(MyPacket, HeaderBufferSize); // // Copy packet flags. // NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); // // Force protocols above to make a copy if they want to hang // on to data in this packet. This is because we are in our // Receive handler (not ReceivePacket), and the original // packet can't be accessed after we return from here. // NDIS_SET_PACKET_STATUS(MyPacket, NDIS_STATUS_RESOURCES); // // Set our context information in the packet. Since // the original packet from the miniport below is not being // queued up, set this to NULL: // pRecvReserved = MUX_RSVD_FROM_RECV_PACKET(MyPacket); pRecvReserved->pOriginalPacket = NULL; MUX_INCR_STATISTICS64(&pVElan->GoodReceives); // // By setting NDIS_STATUS_RESOURCES, we also know that // we can reclaim this packet as soon as the call to // NdisMIndicateReceivePacket returns. // NdisMIndicateReceivePacket(pVElan->MiniportAdapterHandle, &MyPacket, 1); // // Reclaim the indicated packet. Since we had set its status // to NDIS_STATUS_RESOURCES, we are guaranteed that protocols // above are done with it. Our ReturnPacket handler will // not be called for this packet, so call it ourselves. // MPReturnPacket((NDIS_HANDLE)pVElan, MyPacket); // // Done with this VELAN. // continue; } // // else... // // Failed to allocate a packet to indicate up - fall through. // We will still indicate up using the non-packet API, but // other per-packet/OOB information won't be available // to protocols above. // } else { // // The miniport below us uses the old-style (not packet) // receive indication. Fall through. // } // // Fall through to here if the miniport below us has // either not indicated an NDIS_PACKET or we could not // allocate one. // // // Mark the VELAN so that we will forward up a receive // complete indication. // pVElan->IndicateRcvComplete = TRUE; #if IEEE_VLAN_SUPPORT // // Get at the EtherType field. // pTpid = (PUSHORT)((PUCHAR)HeaderBuffer + 2 * ETH_LENGTH_OF_ADDRESS); // // Check if the EtherType indicates presence of a tag header. // if (*pTpid == TPID) { pTagHeader = (VLAN_TAG_HEADER UNALIGNED *)LookAheadBuffer; // // Drop this frame if it contains Routing information; // we don't support this. // if (GET_CANONICAL_FORMAT_ID_FROM_TAG(pTagHeader) != 0) { Status = NDIS_STATUS_INVALID_PACKET; MUX_DECR_PENDING_RECEIVES(pVElan); MUX_INCR_STATISTICS(&pVElan->RcvFormatErrors); continue; } // // If there is a VLAN ID in this frame, and we have // a configured VLAN ID for this VELAN, check if they // are the same - drop if not. // if ((GET_VLAN_ID_FROM_TAG(pTagHeader) != (unsigned)0) && (pVElan->VlanId != (unsigned)0) && (ULONG)(GET_VLAN_ID_FROM_TAG(pTagHeader) != pVElan->VlanId)) { Status = NDIS_STATUS_NOT_ACCEPTED; MUX_DECR_PENDING_RECEIVES(pVElan); MUX_INCR_STATISTICS(&pVElan->RcvVlanIdErrors); continue; } // // Copy information from the tag header to per-packet // info fields. // MuxRcvContext.NdisPacket8021QInfo.Value = NULL; COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO( MuxRcvContext.NdisPacket8021QInfo, pTagHeader); // // Prepare for indicating up this frame (the tag // header must be removed). First, copy in the real // EtherType value from the tag header. // *pTpid = *((PUSHORT)((PUCHAR)LookAheadBuffer + sizeof(pTagHeader->TagInfo))); // // Account for removing the tag header. // LookAheadBuffer = (PVOID)((PUCHAR)LookAheadBuffer + VLAN_TAG_HEADER_SIZE); LookAheadBufferSize -= VLAN_TAG_HEADER_SIZE; PacketSize -= VLAN_TAG_HEADER_SIZE; // // Use MuxRcvContext to store context for the receive, // to be used in MpTransferData, if called. // MuxRcvContext.TagHeaderLen = VLAN_TAG_HEADER_SIZE; } else { MuxRcvContext.TagHeaderLen = 0; } MuxRcvContext.MacRcvContext = MacReceiveContext; // // In order not to change the code a lot // MacReceiveContext = &MuxRcvContext; #endif MUX_INCR_STATISTICS64(&pVElan->GoodReceives); // // Indicate receive using the non-packet API. // NdisMEthIndicateReceive(pVElan->MiniportAdapterHandle, MacReceiveContext, HeaderBuffer, HeaderBufferSize, LookAheadBuffer, LookAheadBufferSize, PacketSize); MUX_DECR_PENDING_RECEIVES(pVElan); } // for (each VELAN) MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } while(FALSE); return Status; } VOID PtReceiveComplete( IN NDIS_HANDLE ProtocolBindingContext ) /*++ Routine Description: Called by the adapter below us when it is done indicating a batch of received packets. We forward this up on all VELANs that need this indication. Arguments: ProtocolBindingContext Pointer to our adapter structure. Return Value: None --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; PLIST_ENTRY p; PVELAN pVElan; LOCK_STATE LockState; MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); if (pVElan->IndicateRcvComplete) { pVElan->IndicateRcvComplete = FALSE; NdisMEthIndicateReceiveComplete(pVElan->MiniportAdapterHandle); } } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } INT PtReceivePacket( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET Packet ) /*++ Routine Description: ReceivePacket handler. Called by NDIS if the miniport below supports NDIS 4.0 style receives. Re-package the buffer chain in a new packet and indicate the new packet to interested protocols above us. Arguments: ProtocolBindingContext - Pointer to our adapter structure. Packet - Pointer to the packet Return Value: == 0 -> We are done with the packet != 0 -> We will keep the packet and call NdisReturnPackets() this many times when done. --*/ { PADAPT pAdapt = (PADAPT)ProtocolBindingContext; PVELAN pVElan; PLIST_ENTRY p; NDIS_STATUS Status; NDIS_STATUS PacketStatus; PNDIS_PACKET MyPacket; PUCHAR pData; PNDIS_BUFFER pNdisBuffer; UINT FirstBufferLength; UINT TotalLength; PUCHAR pDstMac; BOOLEAN bIsMulticast, bIsBroadcast; PMUX_RECV_RSVD pRecvReserved; ULONG ReturnCount; LOCK_STATE LockState; ReturnCount = 0; do { if (pAdapt->PacketFilter == 0) { // // We could get receives in the interval between // initiating a request to set the packet filter on // the binding to 0 and completion of that request. // Drop such packets. // break; } #ifdef NDIS51 // // Collect some information from the packet. // NdisGetFirstBufferFromPacketSafe(Packet, &pNdisBuffer, &pData, &FirstBufferLength, &TotalLength, NormalPagePriority); if (pNdisBuffer == NULL) { // // Out of system resources. Drop this packet. // break; } #else NdisGetFirstBufferFromPacket(Packet, &pNdisBuffer, &pData, &FirstBufferLength, &TotalLength); #endif pDstMac = pData; bIsMulticast = ETH_IS_MULTICAST(pDstMac); bIsBroadcast = ETH_IS_BROADCAST(pDstMac); // // Lock down the VELAN list on the adapter so that // no insertions/deletions to this list happen while // we loop through it. The packet filter will also not // change during the time we hold the read lock. // MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { BOOLEAN bIndicateReceive; pVElan = CONTAINING_RECORD(p, VELAN, Link); // // Should the packet be indicated up on this VELAN? // bIndicateReceive = PtMatchPacketToVElan(pVElan, pDstMac, bIsMulticast, bIsBroadcast); if (!bIndicateReceive) { continue; } // // Make sure we don't Halt the VELAN miniport while // we are accessing it here. See MPHalt. // // Also don't indicate receives if the virtual miniport // has been set to a low power state. A specific case // is when the system is resuming from "Stand-by", if // the lower adapter is restored to D0 before the upper // miniports are. // MUX_INCR_PENDING_RECEIVES(pVElan); if ((pVElan->MiniportHalting) || (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))) { MUX_DECR_PENDING_RECEIVES(pVElan); continue; } // // Get a packet off the pool and indicate that up // NdisDprAllocatePacket(&Status, &MyPacket, pVElan->RecvPacketPoolHandle); if (Status == NDIS_STATUS_SUCCESS) { PacketStatus = NDIS_GET_PACKET_STATUS(Packet); pRecvReserved = MUX_RSVD_FROM_RECV_PACKET(MyPacket); if (PacketStatus != NDIS_STATUS_RESOURCES) { pRecvReserved->pOriginalPacket = Packet; } else { // // This will ensure we don't call NdisReturnPacket for the packet if the packet // status is NDIS_STATUS_RESOURCES // pRecvReserved->pOriginalPacket = NULL; } MyPacket->Private.Head = Packet->Private.Head; MyPacket->Private.Tail = Packet->Private.Tail; // // Get the original packet (it could be the same // packet as the one received or a different one // based on the number of layered miniports below) // and set it on the indicated packet so the OOB // data is visible correctly to protocols above us. // NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet)); // // Copy Packet Flags // NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet); NDIS_SET_PACKET_STATUS(MyPacket, PacketStatus); NDIS_SET_PACKET_HEADER_SIZE(MyPacket, NDIS_GET_PACKET_HEADER_SIZE(Packet)); #if IEEE_VLAN_SUPPORT Status = PtHandleRcvTagging(pVElan, Packet, MyPacket); if (Status != NDIS_STATUS_SUCCESS) { NdisFreePacket(MyPacket); MUX_DECR_PENDING_RECEIVES(pVElan); continue; } #endif MUX_INCR_STATISTICS64(&pVElan->GoodReceives); // // Indicate it up. // if (PacketStatus != NDIS_STATUS_RESOURCES) { ReturnCount++; } NdisMIndicateReceivePacket(pVElan->MiniportAdapterHandle, &MyPacket, 1); // // Check if we had indicated up the packet with // status set to NDIS_STATUS_RESOURCES. // // NOTE -- do not use NDIS_GET_PACKET_STATUS(MyPacket) // for this since it might have changed! Use the value // saved in the local variable. // if (PacketStatus == NDIS_STATUS_RESOURCES) { // // Our ReturnPackets handler will not be called // for this packet. We should reclaim it right here. // MPReturnPacket((NDIS_HANDLE)pVElan, MyPacket); } } else { // // Failed to allocate a packet. // MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors); MUX_DECR_PENDING_RECEIVES(pVElan); } } // for (loop thru all VELANs) MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } while (FALSE); // // Return the # of receive indications made for this packet. // We will call NdisReturnPackets for this packet as many // times (see MPReturnPackets). // return (ReturnCount); } NDIS_STATUS PtPnPNetEventSetPower( IN PADAPT pAdapt, IN PNET_PNP_EVENT pNetPnPEvent ) /*++ Routine Description: This is a notification to our protocol edge of the power state of the lower miniport. If it is going to a low-power state, we must wait here for all outstanding sends and requests to complete. Arguments: pAdapt - Pointer to the adpater structure pNetPnPEvent - The Net Pnp Event. this contains the new device state Return Value: NDIS_STATUS_SUCCESS --*/ { PLIST_ENTRY p; PVELAN pVElan; LOCK_STATE LockState; NDIS_STATUS Status; // // Store the new power state. // pAdapt->PtDevicePowerState = *(PNDIS_DEVICE_POWER_STATE)pNetPnPEvent->Buffer; DBGPRINT(MUX_LOUD, ("PnPNetEventSetPower: Adapt %p, SetPower to %d\n", pAdapt, pAdapt->PtDevicePowerState)); // // Check if the miniport below is going to a low power state. // if (MUX_IS_LOW_POWER_STATE(pAdapt->PtDevicePowerState)) { ULONG i; // // It is going to a low power state. Wait for outstanding // I/O to complete on the adapter. // for (i = 0; i < 10000; i++) { MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); if ((pVElan->OutstandingSends != 0) || (pVElan->OutstandingReceives != 0)) { break; } } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); if (p == &pAdapt->VElanList) { // // There are no VELANs with pending I/O. // break; } DBGPRINT(MUX_INFO, ("SetPower: Adapt %p, waiting for pending IO to complete\n", pAdapt)); NdisMSleep(1000); } } else { // // The device below is powered on. If we had requests // pending on any VELANs, send them down now. // MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pVElan = CONTAINING_RECORD(p, VELAN, Link); // // Need to make sure other threads do not try to acquire the write lock while holding // the same spin lock // NdisAcquireSpinLock(&pVElan->Lock); if (pVElan->QueuedRequest) { pVElan->QueuedRequest = FALSE; NdisReleaseSpinLock(&pVElan->Lock); NdisRequest(&Status, pAdapt->BindingHandle, &pVElan->Request.Request); if (Status != NDIS_STATUS_PENDING) { PtRequestComplete(pAdapt, &pVElan->Request.Request, Status); } } else { NdisReleaseSpinLock(&pVElan->Lock); } } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } return (NDIS_STATUS_SUCCESS); } NDIS_STATUS PtPNPHandler( IN NDIS_HANDLE ProtocolBindingContext, IN PNET_PNP_EVENT pNetPnPEvent ) /*++ Routine Description: This is called by NDIS to notify us of a PNP event related to a lower binding. Based on the event, this dispatches to other helper routines. Arguments: ProtocolBindingContext - Pointer to our adapter structure. Can be NULL for "global" notifications pNetPnPEvent - Pointer to the PNP event to be processed. Return Value: NDIS_STATUS code indicating status of event processing. --*/ { PADAPT pAdapt =(PADAPT)ProtocolBindingContext; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PLIST_ENTRY p; DBGPRINT(MUX_LOUD, ("PtPnPHandler: Adapt %p, NetPnPEvent %d\n", pAdapt, pNetPnPEvent->NetEvent)); switch (pNetPnPEvent->NetEvent) { case NetEventSetPower: Status = PtPnPNetEventSetPower(pAdapt, pNetPnPEvent); break; case NetEventReconfigure: // // Rescan configuration and bring up any VELANs that // have been newly added. Make sure that the global // adapter list is undisturbed while we traverse it. // MUX_ACQUIRE_MUTEX(&GlobalMutex); for (p = AdapterList.Flink; p != &AdapterList; p = p->Flink) { pAdapt = CONTAINING_RECORD(p, ADAPT, Link); PtBootStrapVElans(pAdapt); } MUX_RELEASE_MUTEX(&GlobalMutex); Status = NDIS_STATUS_SUCCESS; break; default: Status = NDIS_STATUS_SUCCESS; break; } return Status; } NDIS_STATUS PtCreateAndStartVElan( IN PADAPT pAdapt, IN PNDIS_STRING pVElanKey ) /*++ Routine Description: Create and start a VELAN with the given key name. Check if a VELAN with this key name already exists; if so do nothing. ASSUMPTION: this is called from either the BindAdapter handler for the underlying adapter, or from the PNP reconfig handler. Both these routines are protected by NDIS against pre-emption by UnbindAdapter. If this routine will be called from any other context, it should be protected against a simultaneous call to our UnbindAdapter handler. Arguments: pAdapt - Pointer to Adapter structure pVElanKey - Points to a Unicode string naming the VELAN to create. Return Value: NDIS_STATUS_SUCCESS if we either found a duplicate VELAN or successfully initiated a new ELAN with the given key. NDIS_STATUS_XXX error code otherwise (failure initiating a new VELAN). --*/ { NDIS_STATUS Status; PVELAN pVElan; Status = NDIS_STATUS_SUCCESS; pVElan = NULL; DBGPRINT(MUX_LOUD, ("=> Create VElan: Adapter %p, ElanKey %ws\n", pAdapt, pVElanKey->Buffer)); do { // // Weed out duplicates. // if (pVElanKey != NULL) { pVElan = PtFindVElan(pAdapt, pVElanKey); if (NULL != pVElan) { // // Duplicate - bail out silently. // DBGPRINT(MUX_WARN, ("CreateElan: found duplicate pVElan %x\n", pVElan)); Status = NDIS_STATUS_SUCCESS; pVElan = NULL; break; } } pVElan = PtAllocateAndInitializeVElan(pAdapt, pVElanKey); if (pVElan == NULL) { Status = NDIS_STATUS_RESOURCES; break; } // // Request NDIS to initialize the virtual miniport. Set // the flag below just in case an unbind occurs before // MiniportInitialize is called. // pVElan->MiniportInitPending = TRUE; NdisInitializeEvent(&pVElan->MiniportInitEvent); Status = NdisIMInitializeDeviceInstanceEx(DriverHandle, &pVElan->CfgDeviceName, pVElan); if (Status != NDIS_STATUS_SUCCESS) { PtUnlinkVElanFromAdapter(pVElan); // IMInit failed pVElan = NULL; break; } } while (FALSE); DBGPRINT(MUX_INFO, ("<= Create VElan: Adapter %p, VELAN %p\n", pAdapt, pVElan)); return Status; } PVELAN PtAllocateAndInitializeVElan( IN PADAPT pAdapt, IN PNDIS_STRING pVElanKey ) /*++ Routine Description: Allocates and initializes a VELAN structure. Also links it to the specified ADAPT. Arguments: pAdapt - Adapter to link VELAN to pVElanKey - Key to the VELAN Return Value: Pointer to VELAN structure if successful, NULL otherwise. --*/ { PVELAN pVElan; ULONG Length; NDIS_STATUS Status; LOCK_STATE LockState; pVElan = NULL; Status = NDIS_STATUS_SUCCESS; do { Length = sizeof(VELAN) + pVElanKey->Length + sizeof(WCHAR); // // Allocate a VELAN data structure. // NdisAllocateMemoryWithTag(&pVElan, Length, TAG); if (pVElan == NULL) { DBGPRINT(MUX_FATAL, ("AllocateVElan: Failed to allocate %d bytes for VELAN\n", Length)); Status = NDIS_STATUS_RESOURCES; break; } // // Initialize it. // NdisZeroMemory(pVElan, Length); NdisInitializeListHead(&pVElan->Link); // // Initialize the built-in request structure to signify // that it is used to forward NDIS requests. // pVElan->Request.pVElan = pVElan; NdisInitializeEvent(&pVElan->Request.Event); // // Store in the key name. // pVElan->CfgDeviceName.Length = 0; pVElan->CfgDeviceName.Buffer = (PWCHAR)((PUCHAR)pVElan + sizeof(VELAN)); pVElan->CfgDeviceName.MaximumLength = pVElanKey->MaximumLength + sizeof(WCHAR); (VOID)NdisUpcaseUnicodeString(&pVElan->CfgDeviceName, pVElanKey); pVElan->CfgDeviceName.Buffer[pVElanKey->Length/sizeof(WCHAR)] = ((WCHAR)0); // // Initialize LastIndicatedStatus to media connect // pVElan->LastIndicatedStatus = NDIS_STATUS_MEDIA_CONNECT; // // Set power state of virtual miniport to D0. // pVElan->MPDevicePowerState = NdisDeviceStateD0; // // Cache the binding handle for quick reference. // pVElan->BindingHandle = pAdapt->BindingHandle; pVElan->pAdapt = pAdapt; // // Copy in some adapter parameters. // pVElan->LookAhead = pAdapt->MaxLookAhead; pVElan->LinkSpeed = pAdapt->LinkSpeed; NdisMoveMemory(pVElan->PermanentAddress, pAdapt->CurrentAddress, sizeof(pVElan->PermanentAddress)); NdisMoveMemory(pVElan->CurrentAddress, pAdapt->CurrentAddress, sizeof(pVElan->CurrentAddress)); DBGPRINT(MUX_LOUD, ("Alloced VELAN %p, MAC addr %s\n", pVElan, MacAddrToString(pVElan->CurrentAddress))); NdisAllocateSpinLock(&pVElan->Lock); #if IEEE_VLAN_SUPPORT // // Allocate lookaside list for tag headers. // NdisInitializeNPagedLookasideList ( &pVElan->TagLookaside, NULL, NULL, 0, ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE, 'TxuM', 0); #endif // // Allocate a packet pool for sends. // NdisAllocatePacketPoolEx(&Status, &pVElan->SendPacketPoolHandle, MIN_PACKET_POOL_SIZE, MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE, sizeof(MUX_SEND_RSVD)); if (Status != NDIS_STATUS_SUCCESS) { DBGPRINT(MUX_FATAL, ("PtAllocateVElan: failed to allocate send packet pool\n")); break; } // // NOTE: this sample driver does not -originate- packets in the // send or receive directions. If the driver must originate packets, // here is a good place to allocate NDIS buffer pool(s) for // this purpose. // #if IEEE_VLAN_SUPPORT // // Allocate a buffer pool for tag headers. // NdisAllocateBufferPool (&Status, &pVElan->BufferPoolHandle, MIN_PACKET_POOL_SIZE); ASSERT(Status == NDIS_STATUS_SUCCESS); #endif // // Allocate a packet pool for receives. // NdisAllocatePacketPoolEx(&Status, &pVElan->RecvPacketPoolHandle, MIN_PACKET_POOL_SIZE, MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE, PROTOCOL_RESERVED_SIZE_IN_PACKET); if (Status != NDIS_STATUS_SUCCESS) { DBGPRINT(MUX_FATAL, ("PtAllocateVElan: failed to allocate receive packet pool\n")); break; } // // Finally link this VELAN to the Adapter's VELAN list. // PtReferenceVElan(pVElan, (PUCHAR)"adapter"); MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); PtReferenceAdapter(pAdapt, (PUCHAR)"VElan"); InsertTailList(&pAdapt->VElanList, &pVElan->Link); pAdapt->VElanCount++; pVElan->VElanNumber = NdisInterlockedIncrement((PLONG)&NextVElanNumber); MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); } while (FALSE); if (Status != NDIS_STATUS_SUCCESS) { if (pVElan) { PtDeallocateVElan(pVElan); pVElan = NULL; } } return (pVElan); } VOID PtDeallocateVElan( IN PVELAN pVElan ) /*++ Routine Description: Free up all resources allocated to a VELAN, and then the VELAN structure itself. Arguments: pVElan - Pointer to VELAN to be deallocated. Return Value: None --*/ { if (pVElan->SendPacketPoolHandle != NULL) { NdisFreePacketPool(pVElan->SendPacketPoolHandle); } if (pVElan->RecvPacketPoolHandle != NULL) { NdisFreePacketPool(pVElan->RecvPacketPoolHandle); } #if IEEE_VLAN_SUPPORT NdisFreeBufferPool(pVElan->BufferPoolHandle); NdisDeleteNPagedLookasideList(&pVElan->TagLookaside); #endif NdisFreeMemory(pVElan, 0, 0); } VOID PtStopVElan( IN PVELAN pVElan ) /*++ Routine Description: Stop a VELAN by requesting NDIS to halt the virtual miniport. The caller has a reference on the VELAN, so it won't go away while we are executing in this routine. ASSUMPTION: this is only called in the context of unbinding from the underlying miniport. If it may be called from elsewhere, this should protect itself from re-entrancy. Arguments: pVElan - Pointer to VELAN to be stopped. Return Value: None --*/ { NDIS_STATUS Status; NDIS_HANDLE MiniportAdapterHandle; BOOLEAN bMiniportInitCancelled = FALSE; DBGPRINT(MUX_LOUD, ("=> StopVElan: VELAN %p, Adapt %p\n", pVElan, pVElan->pAdapt)); // // We make blocking calls below. // ASSERT_AT_PASSIVE(); // // If there was a queued request on this VELAN, fail it now. // NdisAcquireSpinLock(&pVElan->Lock); ASSERT(pVElan->DeInitializing == TRUE); if (pVElan->QueuedRequest) { pVElan->QueuedRequest = FALSE; NdisReleaseSpinLock(&pVElan->Lock); PtRequestComplete(pVElan->pAdapt, &pVElan->Request.Request, NDIS_STATUS_FAILURE); } else { NdisReleaseSpinLock(&pVElan->Lock); } // // Check if we had called NdisIMInitializeDeviceInstanceEx and // we are awaiting a call to MiniportInitialize. // if (pVElan->MiniportInitPending) { // // Attempt to cancel miniport init. // Status = NdisIMCancelInitializeDeviceInstance( DriverHandle, &pVElan->CfgDeviceName); if (Status == NDIS_STATUS_SUCCESS) { // // Successfully cancelled IM initialization; our // Miniport Init routine will not be called for this // VELAN miniport. // pVElan->MiniportInitPending = FALSE; ASSERT(pVElan->MiniportAdapterHandle == NULL); bMiniportInitCancelled = TRUE; } else { // // Our Miniport Initialize routine will be called // (may be running on another thread at this time). // Wait for it to finish. // NdisWaitEvent(&pVElan->MiniportInitEvent, 0); ASSERT(pVElan->MiniportInitPending == FALSE); } } // // Check if Miniport Init has run. If so, deinitialize the virtual // miniport. This will result in a call to our Miniport Halt routine, // where the VELAN will be cleaned up. // MiniportAdapterHandle = pVElan->MiniportAdapterHandle; if ((NULL != MiniportAdapterHandle) && (!pVElan->MiniportHalting)) { // // The miniport was initialized, and has not yet halted. // ASSERT(bMiniportInitCancelled == FALSE); (VOID)NdisIMDeInitializeDeviceInstance(MiniportAdapterHandle); } else { if (bMiniportInitCancelled) { // // No NDIS events can come to this VELAN since it // was never initialized as a miniport. We need to unlink // it explicitly here. // PtUnlinkVElanFromAdapter(pVElan); } } } VOID PtUnlinkVElanFromAdapter( IN PVELAN pVElan ) /*++ Routine Description: Utility routine to unlink a VELAN from its parent ADAPT structure. Arguments: pVElan - Pointer to VELAN to be unlinked. Return Value: None --*/ { PADAPT pAdapt = pVElan->pAdapt; LOCK_STATE LockState; ASSERT(pAdapt != NULL); // // Remove this VELAN from the Adapter list // MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); RemoveEntryList(&pVElan->Link); pAdapt->VElanCount--; MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); pVElan->pAdapt = NULL; PtDereferenceVElan(pVElan, (PUCHAR)"adapter"); PtDereferenceAdapter(pAdapt, (PUCHAR)"VElan"); } PVELAN PtFindVElan( IN PADAPT pAdapt, IN PNDIS_STRING pVElanKey ) /*++ Routine Description: Find an ELAN by bind name/key Arguments: pAdapt - Pointer to an adapter struct. pVElanKey - The VELAN's device name Return Value: Pointer to matching VELAN or NULL if not found. --*/ { PLIST_ENTRY p; PVELAN pVElan; BOOLEAN Found; NDIS_STRING VElanKeyName; LOCK_STATE LockState; ASSERT_AT_PASSIVE(); DBGPRINT(MUX_LOUD, ("FindElan: Adapter %p, ElanKey %ws\n", pAdapt, pVElanKey->Buffer)); pVElan = NULL; Found = FALSE; VElanKeyName.Buffer = NULL; do { // // Make an up-cased copy of the given string. // NdisAllocateMemoryWithTag(&VElanKeyName.Buffer, pVElanKey->MaximumLength, TAG); if (VElanKeyName.Buffer == NULL) { break; } VElanKeyName.Length = pVElanKey->Length; VElanKeyName.MaximumLength = pVElanKey->MaximumLength; (VOID)NdisUpcaseUnicodeString(&VElanKeyName, pVElanKey); // // Go through all VELANs on the ADAPT structure, looking // for a VELAN that has a matching device name. // MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState); p = pAdapt->VElanList.Flink; while (p != &pAdapt->VElanList) { pVElan = CONTAINING_RECORD(p, VELAN, Link); if ((VElanKeyName.Length == pVElan->CfgDeviceName.Length) && (memcmp(VElanKeyName.Buffer, pVElan->CfgDeviceName.Buffer, VElanKeyName.Length) == 0)) { Found = TRUE; break; } p = p->Flink; } MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState); } while (FALSE); if (!Found) { DBGPRINT(MUX_INFO, ( "FindElan: No match found!\n")); pVElan = NULL; } if (VElanKeyName.Buffer) { NdisFreeMemory(VElanKeyName.Buffer, VElanKeyName.Length, 0); } return pVElan; } NDIS_STATUS PtBootStrapVElans( IN PADAPT pAdapt ) /*++ Routine Description: Start up the VELANs configured for an adapter. Arguments: pAdapt - Pointer to ATMLANE Adapter structure Return Value: None --*/ { NDIS_STATUS Status; NDIS_HANDLE AdapterConfigHandle; PNDIS_CONFIGURATION_PARAMETER Param; NDIS_STRING DeviceStr = NDIS_STRING_CONST("UpperBindings"); PWSTR buffer; LOCK_STATE LockState; // // Initialize. // Status = NDIS_STATUS_SUCCESS; AdapterConfigHandle = NULL; do { DBGPRINT(MUX_LOUD, ("BootStrapElans: Starting ELANs on adapter %x\n", pAdapt)); // // Open the protocol configuration section for this adapter. // NdisOpenProtocolConfiguration(&Status, &AdapterConfigHandle, &pAdapt->ConfigString); if (NDIS_STATUS_SUCCESS != Status) { AdapterConfigHandle = NULL; DBGPRINT(MUX_ERROR, ("BootStrapElans: OpenProtocolConfiguration failed\n")); Status = NDIS_STATUS_OPEN_FAILED; break; } // // Read the "UpperBindings" reserved key that contains a list // of device names representing our miniport instances corresponding // to this lower binding. The UpperBindings is a // MULTI_SZ containing a list of device names. We will loop through // this list and initialize the virtual miniports. // NdisReadConfiguration(&Status, &Param, AdapterConfigHandle, &DeviceStr, NdisParameterMultiString); if (NDIS_STATUS_SUCCESS != Status) { DBGPRINT(MUX_ERROR, ("BootStrapElans: NdisReadConfiguration failed\n")); break; } // // Parse the Multi_sz string to extract the device name of each VELAN. // This is used as the key name for the VELAN. // buffer = (PWSTR)Param->ParameterData.StringData.Buffer; while(*buffer != L'\0') { NDIS_STRING DeviceName; NdisInitUnicodeString(&DeviceName, buffer); Status = PtCreateAndStartVElan(pAdapt, &DeviceName); if (NDIS_STATUS_SUCCESS != Status) { DBGPRINT(MUX_ERROR, ("BootStrapElans: CreateVElan failed\n")); break; } buffer = (PWSTR)((PUCHAR)buffer + DeviceName.Length + sizeof(WCHAR)); }; } while (FALSE); // // Close config handles // if (NULL != AdapterConfigHandle) { NdisCloseConfiguration(AdapterConfigHandle); } // // If the driver cannot create any velan for the adapter // if (Status != NDIS_STATUS_SUCCESS) { MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); // // No VElan is created for this adapter // if (pAdapt->VElanCount != 0) { Status = NDIS_STATUS_SUCCESS; } MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); } return Status; } VOID PtReferenceVElan( IN PVELAN pVElan, IN PUCHAR String ) /*++ Routine Description: Add a references to an Elan structure. Arguments: pElan - Pointer to the Elan structure. Return Value: None. --*/ { NdisInterlockedIncrement((PLONG)&pVElan->RefCount); #if !DBG UNREFERENCED_PARAMETER(String); #endif DBGPRINT(MUX_LOUD, ("ReferenceElan: Elan %p (%s) new count %d\n", pVElan, String, pVElan->RefCount)); return; } ULONG PtDereferenceVElan( IN PVELAN pVElan, IN PUCHAR String ) /*++ Routine Description: Subtract a reference from an VElan structure. If the reference count becomes zero, deallocate it. Arguments: pElan - Pointer to an VElan structure. Return Value: None. --*/ { ULONG rc; #if !DBG UNREFERENCED_PARAMETER(String); #endif ASSERT(pVElan->RefCount > 0); rc = NdisInterlockedDecrement((PLONG)&pVElan->RefCount); if (rc == 0) { // // Free memory if there is no outstanding reference. // Note: Length field is not required if the memory // is allocated with NdisAllocateMemoryWithTag. // PtDeallocateVElan(pVElan); } DBGPRINT(MUX_LOUD, ("DereferenceElan: VElan %p (%s) new count %d\n", pVElan, String, rc)); return (rc); } BOOLEAN PtReferenceAdapter( IN PADAPT pAdapt, IN PUCHAR String ) /*++ Routine Description: Add a references to an Adapter structure. Arguments: pAdapt - Pointer to the Adapter structure. Return Value: None. --*/ { #if !DBG UNREFERENCED_PARAMETER(String); #endif NdisInterlockedIncrement((PLONG)&pAdapt->RefCount); DBGPRINT(MUX_LOUD, ("ReferenceAdapter: Adapter %x (%s) new count %d\n", pAdapt, String, pAdapt->RefCount)); return TRUE; } ULONG PtDereferenceAdapter( IN PADAPT pAdapt, IN PUCHAR String ) /*++ Routine Description: Subtract a reference from an Adapter structure. If the reference count becomes zero, deallocate it. Arguments: pAdapt - Pointer to an adapter structure. Return Value: None. --*/ { ULONG rc; #if !DBG UNREFERENCED_PARAMETER(String); #endif ASSERT(pAdapt->RefCount > 0); rc = NdisInterlockedDecrement ((PLONG)&pAdapt->RefCount); if (rc == 0) { // // Free memory if there is no outstanding reference. // Note: Length field is not required if the memory // is allocated with NdisAllocateMemoryWithTag. // NdisFreeMemory(pAdapt, 0, 0); } DBGPRINT(MUX_LOUD, ("DereferenceAdapter: Adapter %x (%s) new count %d\n", pAdapt, String, rc)); return (rc); } #if IEEE_VLAN_SUPPORT NDIS_STATUS PtHandleRcvTagging( IN PVELAN pVElan, IN PNDIS_PACKET Packet, IN OUT PNDIS_PACKET MyPacket ) /*++ Routine Description: Parse a received Ethernet frame for 802.1Q tag information. If a tag header is present, copy in relevant field values to per-packet information to the new packet (MyPacket) used to indicate up this frame. Arguments: pVElan - Pointer to the VELAN structure. Packet - Pointer to the indicated packet from the lower miniport MyPacket - Pointer to the new allocated packet Return Value: NDIS_STATUS_SUCCESS if the frame was successfully parsed and hence should be indicated up this VELAN. NDIS_STATUS_XXX otherwise. --*/ { VLAN_TAG_HEADER UNALIGNED * pTagHeader; USHORT UNALIGNED * pTpid; PVOID pVa; ULONG BufferLength; PNDIS_BUFFER pNdisBuffer; NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo; PVOID pDst; BOOLEAN OnlyOneBuffer = FALSE; NDIS_STATUS Status; Status = NDIS_STATUS_SUCCESS; do { // // If the vlan ID the virtual miniport is 0, the miniport should act like it doesn't support // VELAN tag processing // if (pVElan->VlanId == 0) { break; } pNdisBuffer = Packet->Private.Head; #ifdef NDIS51_MINIPORT NdisQueryBufferSafe(pNdisBuffer, &pVa, (PUINT)&BufferLength, NormalPagePriority ); if (pVa == NULL) { Status = NDIS_STATUS_RESOURCES; MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors); break; } #else NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength); #endif // // The first NDIS buffer (lookahead) must be longer than // ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE // ASSERT(BufferLength >= ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE); // // Get at the EtherType field. // pTpid = (USHORT UNALIGNED *)((PUCHAR)pVa + 2 * ETH_LENGTH_OF_ADDRESS); // // Check if a tag header is present. // if (*pTpid != TPID) { // // No tag header exists - nothing more to do here. // NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) = 0; break; } // // We do have a tag header. Parse it further. // // // If E-RIF is present, discard the packet - we don't // support this variation. // pTagHeader = (VLAN_TAG_HEADER UNALIGNED *)(pTpid + 1); if (GET_CANONICAL_FORMAT_ID_FROM_TAG(pTagHeader) != 0) { // // Drop the packet // Status = NDIS_STATUS_NOT_ACCEPTED; MUX_INCR_STATISTICS(&pVElan->RcvFormatErrors); break; } // // If there is a VLAN ID in this frame, and we have // a configured VLAN ID for this VELAN, check if they // are the same - drop if not. // if ((GET_VLAN_ID_FROM_TAG(pTagHeader) != 0) && (GET_VLAN_ID_FROM_TAG(pTagHeader) != pVElan->VlanId)) { Status = NDIS_STATUS_NOT_ACCEPTED; MUX_INCR_STATISTICS(&pVElan->RcvVlanIdErrors); break; } // // Parsed this frame successfully. Copy in relevant // parts of the tag header to per-packet information. // NdisPacket8021qInfo.Value = NULL; // initialize COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(NdisPacket8021qInfo, pTagHeader); NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) = NdisPacket8021qInfo.Value; // // Strip off the tag header "in place": // pDst = (PVOID)((PUCHAR)pVa + VLAN_TAG_HEADER_SIZE); RtlMoveMemory(pDst, pVa, 2 * ETH_LENGTH_OF_ADDRESS); // // Allocate a new buffer to describe the new first // buffer in the packet. This could very well be the // only buffer in the packet. // NdisAllocateBuffer(&Status, &pNdisBuffer, pVElan->BufferPoolHandle, pDst, BufferLength - VLAN_TAG_HEADER_SIZE); if (Status != NDIS_STATUS_SUCCESS) { // // Drop the packet // Status = NDIS_STATUS_RESOURCES; MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors); break; } // // Prepare the new packet to be indicated up: this consists // of the buffer chain starting with the second buffer, // appended to the first buffer set up in the previous step. // MyPacket->Private.Head = NDIS_BUFFER_LINKAGE(Packet->Private.Head); // // Only one buffer in the packet // if (MyPacket->Private.Head == NULL) { OnlyOneBuffer = TRUE; } NdisChainBufferAtFront(MyPacket, pNdisBuffer); if (OnlyOneBuffer) { MyPacket->Private.Tail = MyPacket->Private.Head; } else { MyPacket->Private.Tail = Packet->Private.Tail; } } while (FALSE); return Status; } #endif // IEEE_VLAN_SUPPORT