/*++ Copyright (c) 1992-2000 Microsoft Corporation Module Name: miniport.c Abstract: NDIS Miniport Entry points and utility functions for the NDIS MUX Intermediate Miniport sample. The driver exposes zero or more Virtual Ethernet LANs (VELANs) as NDIS miniport instances over each lower (protocol-edge) binding to an underlying adapter. Environment: Kernel mode. Revision History: --*/ #include "precomp.h" #pragma hdrstop #define MODULE_NUMBER MODULE_MINI NDIS_OID VElanSupportedOids[] = { OID_GEN_SUPPORTED_LIST, OID_GEN_HARDWARE_STATUS, OID_GEN_MEDIA_SUPPORTED, OID_GEN_MEDIA_IN_USE, OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAXIMUM_FRAME_SIZE, OID_GEN_LINK_SPEED, OID_GEN_TRANSMIT_BUFFER_SPACE, OID_GEN_RECEIVE_BUFFER_SPACE, OID_GEN_TRANSMIT_BLOCK_SIZE, OID_GEN_RECEIVE_BLOCK_SIZE, OID_GEN_VENDOR_ID, OID_GEN_VENDOR_DESCRIPTION, OID_GEN_VENDOR_DRIVER_VERSION, OID_GEN_CURRENT_PACKET_FILTER, OID_GEN_CURRENT_LOOKAHEAD, OID_GEN_DRIVER_VERSION, OID_GEN_MAXIMUM_TOTAL_SIZE, OID_GEN_PROTOCOL_OPTIONS, OID_GEN_MAC_OPTIONS, OID_GEN_MEDIA_CONNECT_STATUS, OID_GEN_MAXIMUM_SEND_PACKETS, OID_GEN_XMIT_OK, OID_GEN_RCV_OK, OID_GEN_XMIT_ERROR, OID_GEN_RCV_ERROR, OID_GEN_RCV_NO_BUFFER, OID_GEN_RCV_CRC_ERROR, OID_GEN_TRANSMIT_QUEUE_LENGTH, OID_802_3_PERMANENT_ADDRESS, OID_802_3_CURRENT_ADDRESS, OID_802_3_MULTICAST_LIST, OID_802_3_MAXIMUM_LIST_SIZE, OID_802_3_RCV_ERROR_ALIGNMENT, OID_802_3_XMIT_ONE_COLLISION, OID_802_3_XMIT_MORE_COLLISIONS, OID_802_3_XMIT_DEFERRED, OID_802_3_XMIT_MAX_COLLISIONS, OID_802_3_RCV_OVERRUN, OID_802_3_XMIT_UNDERRUN, OID_802_3_XMIT_HEARTBEAT_FAILURE, OID_802_3_XMIT_TIMES_CRS_LOST, OID_802_3_XMIT_LATE_COLLISIONS, OID_PNP_CAPABILITIES, OID_PNP_SET_POWER, OID_PNP_QUERY_POWER, OID_PNP_ADD_WAKE_UP_PATTERN, OID_PNP_REMOVE_WAKE_UP_PATTERN, #if IEEE_VLAN_SUPPORT OID_GEN_VLAN_ID, #endif OID_PNP_ENABLE_WAKE_UP }; NDIS_STATUS MPInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ) /*++ Routine Description: This is the Miniport Initialize routine which gets called as a result of our call to NdisIMInitializeDeviceInstanceEx. The context parameter which we pass there is the VELan structure which we retrieve here. Arguments: OpenErrorStatus Not used by us. SelectedMediumIndex Place-holder for what media we are using MediumArray Array of ndis media passed down to us to pick from MediumArraySize Size of the array MiniportAdapterHandle The handle NDIS uses to refer to us WrapperConfigurationContext For use by NdisOpenConfiguration Return Value: NDIS_STATUS_SUCCESS unless something goes wrong --*/ { UINT i; PVELAN pVElan; NDIS_STATUS Status = NDIS_STATUS_FAILURE; NDIS_MEDIUM Medium; NDIS_HANDLE ConfigurationHandle; PVOID NetworkAddress; #if IEEE_VLAN_SUPPORT PNDIS_CONFIGURATION_PARAMETER Params; NDIS_STRING strVlanId = NDIS_STRING_CONST("VlanID"); #endif // // Start off by retrieving our virtual miniport context (VELAN) and // storing the Miniport handle in it. // pVElan = NdisIMGetDeviceContext(MiniportAdapterHandle); DBGPRINT(MUX_LOUD, ("==> Miniport Initialize: VELAN %p\n", pVElan)); ASSERT(pVElan != NULL); ASSERT(pVElan->pAdapt != NULL); do { pVElan->MiniportAdapterHandle = MiniportAdapterHandle; for (i = 0; i < MediumArraySize; i++) { if (MediumArray[i] == VELAN_MEDIA_TYPE) { *SelectedMediumIndex = i; break; } } if (i == MediumArraySize) { Status = NDIS_STATUS_UNSUPPORTED_MEDIA; break; } // // Access configuration parameters for this miniport. // NdisOpenConfiguration( &Status, &ConfigurationHandle, WrapperConfigurationContext); if (Status != NDIS_STATUS_SUCCESS) { break; } NdisReadNetworkAddress( &Status, &NetworkAddress, &i, ConfigurationHandle); // // If there is a NetworkAddress override, use it // if ((Status == NDIS_STATUS_SUCCESS) && (i == ETH_LENGTH_OF_ADDRESS)) { ETH_COPY_NETWORK_ADDRESS( pVElan->CurrentAddress, NetworkAddress); } else { MPGenerateMacAddr(pVElan); } #if IEEE_VLAN_SUPPORT // // Read VLAN ID // NdisReadConfiguration( &Status, &Params, ConfigurationHandle, &strVlanId, NdisParameterInteger); if (Status == NDIS_STATUS_SUCCESS) { // // Check for out of bound // if (Params->ParameterData.IntegerData > VLAN_ID_MAX) { pVElan->VlanId = VLANID_DEFAULT; } else { pVElan->VlanId = Params->ParameterData.IntegerData; } } else { // // Should fail the initialization or use default value // pVElan->VlanId = VLANID_DEFAULT; Status = NDIS_STATUS_SUCCESS; } #endif NdisCloseConfiguration(ConfigurationHandle); // // Set the attributes now. NDIS_ATTRIBUTE_DESERIALIZE enables us // to make up-calls to NDIS from arbitrary execution contexts. // This also forces us to protect our data structures using // spinlocks where appropriate. Also in this case NDIS does not queue // packets on our behalf. // NdisMSetAttributesEx(MiniportAdapterHandle, pVElan, 0, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT| NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER | NDIS_ATTRIBUTE_DESERIALIZE | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND, 0); // // Create an ioctl interface // (VOID)PtRegisterDevice(); Status = NDIS_STATUS_SUCCESS; } while (FALSE); // // If we had received an UnbindAdapter notification on the underlying // adapter, we would have blocked that thread waiting for the IM Init // process to complete. Wake up any such thread. // // See PtUnbindAdapter for more details. // ASSERT(pVElan->MiniportInitPending == TRUE); pVElan->MiniportInitPending = FALSE; NdisSetEvent(&pVElan->MiniportInitEvent); DBGPRINT(MUX_INFO, ("<== Miniport Initialize: VELAN %p, Status %x\n", pVElan, Status)); return Status; } VOID MPSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) /*++ Routine Description: Send Packet Array handler. Called by NDIS whenever a protocol bound to our VELAN miniport sends one or more packets. We forward each packet to the lower binding. NOTE: NDIS will not Halt this VELAN miniport until all these packets are "send-completed", and we don't unbind the lower binding until all VELANs are halted. Therefore we don't need locks or references on VELAN or ADAPT structures. Arguments: MiniportAdapterContext Pointer to our VELAN PacketArray Set of packets to send NumberOfPackets Length of above array Return Value: None - we call NdisMSendComplete whenever we are done with a packet. --*/ { PVELAN pVElan = (PVELAN)MiniportAdapterContext; PADAPT pAdapt = pVElan->pAdapt; PNDIS_PACKET Packet, MyPacket; NDIS_STATUS Status; PVOID MediaSpecificInfo; ULONG MediaSpecificInfoSize; UINT i; for (i = 0; i < NumberOfPackets; i++) { Packet = PacketArray[i]; // // Allocate a new packet to encapsulate data from the original. // NdisAllocatePacket(&Status, &MyPacket, pVElan->SendPacketPoolHandle); if (Status == NDIS_STATUS_SUCCESS) { PMUX_SEND_RSVD pSendReserved; pSendReserved = MUX_RSVD_FROM_SEND_PACKET(MyPacket); pSendReserved->pOriginalPacket = Packet; pSendReserved->pVElan = pVElan; MyPacket->Private.Flags = NdisGetPacketFlags(Packet) | MUX_SEND_PACKET_FLAGS; MyPacket->Private.Head = Packet->Private.Head; MyPacket->Private.Tail = Packet->Private.Tail; #ifdef WIN9X // // Work around the fact that NDIS does not initialize this // to FALSE on Win9x. // MyPacket->Private.ValidCounts = FALSE; #endif // WIN9X // // Copy OOB data to the new packet. // NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket), NDIS_OOB_DATA_FROM_PACKET(Packet), sizeof(NDIS_PACKET_OOB_DATA)); // // Copy relevant parts of per packet info into the new packet. // #ifndef WIN9X NdisIMCopySendPerPacketInfo(MyPacket, Packet); #endif // // Copy Media specific information. // NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet, &MediaSpecificInfo, &MediaSpecificInfoSize); if (MediaSpecificInfo || MediaSpecificInfoSize) { NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket, MediaSpecificInfo, MediaSpecificInfoSize); } #if IEEE_VLAN_SUPPORT Status = MPHandleSendTagging(pVElan, Packet, MyPacket); if (Status != NDIS_STATUS_SUCCESS) { NdisFreePacket(MyPacket); NdisMSendComplete(pVElan->MiniportAdapterHandle, Packet, Status); continue; } #endif // // Make note of the upcoming send. // MUX_INCR_PENDING_SENDS(pVElan); // // Send it to the lower binding. // NdisSend(&Status, pAdapt->BindingHandle, MyPacket); if (Status != NDIS_STATUS_PENDING) { PtSendComplete((NDIS_HANDLE)pAdapt, MyPacket, Status); } } else { // // Failed to allocate a packet. // break; } } // // If we bailed out above, fail any unprocessed sends. // while (i < NumberOfPackets) { NdisMSendComplete(pVElan->MiniportAdapterHandle, PacketArray[i], NDIS_STATUS_RESOURCES); i++; } } NDIS_STATUS MPQueryInformation( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ) /*++ Routine Description: Entry point called by NDIS to query for the value of the specified OID. All OID values are responded to right here, since this is a virtual device (not pass-through). Arguments: MiniportAdapterContext Pointer to the adapter structure Oid Oid for this query InformationBuffer Buffer for information InformationBufferLength Size of this buffer BytesWritten Specifies how much info is written BytesNeeded In case the buffer is smaller than what we need, tell them how much is needed Return Value: Return code from the NdisRequest below. --*/ { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PVELAN pVElan; NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady; NDIS_MEDIUM Medium = VELAN_MEDIA_TYPE; UCHAR VendorDesc[] = VELAN_VENDOR_DESC; ULONG ulInfo; ULONG64 ulInfo64; USHORT usInfo; UCHAR arrInfo[ETH_LENGTH_OF_ADDRESS]; PVOID pInfo = (PVOID) &ulInfo; ULONG ulInfoLen = sizeof(ulInfo); // Should we forward the request to the miniport below? BOOLEAN bForwardRequest = FALSE; pVElan = (PVELAN) MiniportAdapterContext; // Initialize the result *BytesWritten = 0; *BytesNeeded = 0; switch (Oid) { case OID_GEN_SUPPORTED_LIST: pInfo = (PVOID) VElanSupportedOids; ulInfoLen = sizeof(VElanSupportedOids); break; case OID_GEN_SUPPORTED_GUIDS: // // Do NOT forward this down, otherwise we will // end up with spurious instances of private WMI // classes supported by the lower driver(s). // Status = NDIS_STATUS_NOT_SUPPORTED; break; case OID_GEN_HARDWARE_STATUS: pInfo = (PVOID) &HardwareStatus; ulInfoLen = sizeof(NDIS_HARDWARE_STATUS); break; case OID_GEN_MEDIA_SUPPORTED: case OID_GEN_MEDIA_IN_USE: pInfo = (PVOID) &Medium; ulInfoLen = sizeof(NDIS_MEDIUM); break; case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_MAXIMUM_LOOKAHEAD: ulInfo = pVElan->LookAhead - ETH_HEADER_SIZE; break; case OID_GEN_MAXIMUM_FRAME_SIZE: ulInfo = ETH_MAX_PACKET_SIZE - ETH_HEADER_SIZE; #if IEEE_VLAN_SUPPORT ulInfo -= VLAN_TAG_HEADER_SIZE; #endif break; case OID_GEN_MAXIMUM_TOTAL_SIZE: case OID_GEN_TRANSMIT_BLOCK_SIZE: case OID_GEN_RECEIVE_BLOCK_SIZE: ulInfo = (ULONG) ETH_MAX_PACKET_SIZE; #if IEEE_VLAN_SUPPORT ulInfo -= VLAN_TAG_HEADER_SIZE; #endif break; case OID_GEN_MAC_OPTIONS: ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | NDIS_MAC_OPTION_NO_LOOPBACK; #if IEEE_VLAN_SUPPORT ulInfo |= (NDIS_MAC_OPTION_8021P_PRIORITY | NDIS_MAC_OPTION_8021Q_VLAN); #endif break; case OID_GEN_LINK_SPEED: bForwardRequest = TRUE; break; case OID_GEN_TRANSMIT_BUFFER_SPACE: ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusySends; #if IEEE_VLAN_SUPPORT ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusySends; #endif break; case OID_GEN_RECEIVE_BUFFER_SPACE: ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusyRecvs; #if IEEE_VLAN_SUPPORT ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusyRecvs; #endif break; case OID_GEN_VENDOR_ID: ulInfo = VELAN_VENDOR_ID; break; case OID_GEN_VENDOR_DESCRIPTION: pInfo = VendorDesc; ulInfoLen = sizeof(VendorDesc); break; case OID_GEN_VENDOR_DRIVER_VERSION: ulInfo = VELAN_VENDOR_ID; break; case OID_GEN_DRIVER_VERSION: usInfo = (USHORT) VELAN_DRIVER_VERSION; pInfo = (PVOID) &usInfo; ulInfoLen = sizeof(USHORT); break; case OID_802_3_PERMANENT_ADDRESS: pInfo = pVElan->PermanentAddress; ulInfoLen = ETH_LENGTH_OF_ADDRESS; break; case OID_802_3_CURRENT_ADDRESS: pInfo = pVElan->CurrentAddress; ulInfoLen = ETH_LENGTH_OF_ADDRESS; break; case OID_802_3_MAXIMUM_LIST_SIZE: ulInfo = VELAN_MAX_MCAST_LIST; break; case OID_GEN_MAXIMUM_SEND_PACKETS: ulInfo = VELAN_MAX_SEND_PKTS; break; case OID_GEN_MEDIA_CONNECT_STATUS: // // Get this from the adapter below. // bForwardRequest = TRUE; break; case OID_PNP_QUERY_POWER: // simply succeed this. ulInfoLen = 0; break; case OID_PNP_CAPABILITIES: case OID_PNP_WAKE_UP_PATTERN_LIST: // // Pass down these power management/PNP OIDs. // bForwardRequest = TRUE; break; case OID_GEN_XMIT_OK: ulInfo64 = pVElan->GoodTransmits; pInfo = &ulInfo64; if (InformationBufferLength >= sizeof(ULONG64) || InformationBufferLength == 0) { ulInfoLen = sizeof(ULONG64); } else { ulInfoLen = sizeof(ULONG); } break; case OID_GEN_RCV_OK: ulInfo64 = pVElan->GoodReceives; pInfo = &ulInfo64; if (InformationBufferLength >= sizeof(ULONG64) || InformationBufferLength == 0) { ulInfoLen = sizeof(ULONG64); } else { ulInfoLen = sizeof(ULONG); } break; case OID_GEN_XMIT_ERROR: ulInfo = pVElan->TxAbortExcessCollisions + pVElan->TxDmaUnderrun + pVElan->TxLostCRS + pVElan->TxLateCollisions+ pVElan->TransmitFailuresOther; break; case OID_GEN_RCV_ERROR: ulInfo = pVElan->RcvCrcErrors + pVElan->RcvAlignmentErrors + pVElan->RcvResourceErrors + pVElan->RcvDmaOverrunErrors + pVElan->RcvRuntErrors; #if IEEE_VLAN_SUPPORT ulInfo += (pVElan->RcvVlanIdErrors + pVElan->RcvFormatErrors); #endif break; case OID_GEN_RCV_NO_BUFFER: ulInfo = pVElan->RcvResourceErrors; break; case OID_GEN_RCV_CRC_ERROR: ulInfo = pVElan->RcvCrcErrors; break; case OID_GEN_TRANSMIT_QUEUE_LENGTH: ulInfo = pVElan->RegNumTcb; break; case OID_802_3_RCV_ERROR_ALIGNMENT: ulInfo = pVElan->RcvAlignmentErrors; break; case OID_802_3_XMIT_ONE_COLLISION: ulInfo = pVElan->OneRetry; break; case OID_802_3_XMIT_MORE_COLLISIONS: ulInfo = pVElan->MoreThanOneRetry; break; case OID_802_3_XMIT_DEFERRED: ulInfo = pVElan->TxOKButDeferred; break; case OID_802_3_XMIT_MAX_COLLISIONS: ulInfo = pVElan->TxAbortExcessCollisions; break; case OID_802_3_RCV_OVERRUN: ulInfo = pVElan->RcvDmaOverrunErrors; break; case OID_802_3_XMIT_UNDERRUN: ulInfo = pVElan->TxDmaUnderrun; break; case OID_802_3_XMIT_HEARTBEAT_FAILURE: ulInfo = pVElan->TxLostCRS; break; case OID_802_3_XMIT_TIMES_CRS_LOST: ulInfo = pVElan->TxLostCRS; break; case OID_802_3_XMIT_LATE_COLLISIONS: ulInfo = pVElan->TxLateCollisions; break; #if IEEE_VLAN_SUPPORT case OID_GEN_VLAN_ID: ulInfo = pVElan->VlanId; break; #endif default: Status = NDIS_STATUS_INVALID_OID; break; } if (bForwardRequest == FALSE) { // // No need to forward this request down. // if (Status == NDIS_STATUS_SUCCESS) { if (ulInfoLen <= InformationBufferLength) { // Copy result into InformationBuffer *BytesWritten = ulInfoLen; if(ulInfoLen) { NdisMoveMemory(InformationBuffer, pInfo, ulInfoLen); } } else { // too short *BytesNeeded = ulInfoLen; Status = NDIS_STATUS_BUFFER_TOO_SHORT; } } } else { // // Send this request to the binding below. // Status = MPForwardRequest(pVElan, NdisRequestQueryInformation, Oid, InformationBuffer, InformationBufferLength, BytesWritten, BytesNeeded); } if ((Status != NDIS_STATUS_SUCCESS) && (Status != NDIS_STATUS_PENDING)) { DBGPRINT(MUX_WARN, ("MPQueryInformation VELAN %p, OID 0x%08x, Status = 0x%08x\n", pVElan, Oid, Status)); } return(Status); } NDIS_STATUS MPSetInformation( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: This is the handler for an OID set operation. Relevant OIDs are forwarded down to the lower miniport for handling. Arguments: MiniportAdapterContext Pointer to the adapter structure Oid Oid for this query InformationBuffer Buffer for information InformationBufferLength Size of this buffer BytesRead Specifies how much info is read BytesNeeded In case the buffer is smaller than what we need, tell them how much is needed Return Value: Return code from the NdisRequest below. --*/ { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PVELAN pVElan = (PVELAN) MiniportAdapterContext; ULONG PacketFilter; NDIS_DEVICE_POWER_STATE NewDeviceState; // Should we forward the request to the miniport below? BOOLEAN bForwardRequest = FALSE; *BytesRead = 0; *BytesNeeded = 0; switch (Oid) { // // Let the miniport below handle these OIDs: // case OID_PNP_ADD_WAKE_UP_PATTERN: case OID_PNP_REMOVE_WAKE_UP_PATTERN: case OID_PNP_ENABLE_WAKE_UP: bForwardRequest = TRUE; break; case OID_PNP_SET_POWER: // // Store new power state and succeed the request. // *BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE); if (InformationBufferLength < *BytesNeeded) { Status = NDIS_STATUS_INVALID_LENGTH; break; } NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE)InformationBuffer); // // Check if the VELAN adapter goes from lower power state to D0 // if ((MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) && (!MUX_IS_LOW_POWER_STATE(NewDeviceState))) { // // Indicate the media status is necessary // if (pVElan->LastIndicatedStatus != pVElan->LatestUnIndicateStatus) { NdisMIndicateStatus(pVElan->MiniportAdapterHandle, pVElan->LatestUnIndicateStatus, (PVOID)NULL, 0); NdisMIndicateStatusComplete(pVElan->MiniportAdapterHandle); pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus; } } // // Check if the VELAN adapter goes from D0 to lower power state // if ((!MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) && (MUX_IS_LOW_POWER_STATE(NewDeviceState))) { // // Initialize LastUnIndicateStatus // pVElan->LatestUnIndicateStatus = pVElan->LastIndicatedStatus; } NdisMoveMemory(&pVElan->MPDevicePowerState, InformationBuffer, *BytesNeeded); DBGPRINT(MUX_INFO, ("SetInfo: VElan %p, new miniport power state --- %d\n", pVElan, pVElan->MPDevicePowerState)); break; case OID_802_3_MULTICAST_LIST: Status = MPSetMulticastList(pVElan, InformationBuffer, InformationBufferLength, BytesRead, BytesNeeded); break; case OID_GEN_CURRENT_PACKET_FILTER: if (InformationBufferLength != sizeof(ULONG)) { Status = NDIS_STATUS_INVALID_LENGTH; *BytesNeeded = sizeof(ULONG); break; } NdisMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG)); *BytesRead = sizeof(ULONG); Status = MPSetPacketFilter(pVElan, PacketFilter); break; case OID_GEN_CURRENT_LOOKAHEAD: #if IEEE_VLAN_SUPPORT // // In order to simplify parsing and to avoid excessive // copying, we need the tag header also to be present in the // lookahead buffer. Make sure that the driver below // includes that. // *(PULONG)InformationBuffer += VLAN_TAG_HEADER_SIZE; #endif bForwardRequest = TRUE; break; #if IEEE_VLAN_SUPPORT case OID_GEN_VLAN_ID: if (InformationBufferLength != sizeof(ULONG)) { Status = NDIS_STATUS_INVALID_LENGTH; *BytesNeeded = sizeof(ULONG); break; } NdisMoveMemory(&(pVElan->VlanId), InformationBuffer, sizeof(ULONG)); break; #endif default: Status = NDIS_STATUS_INVALID_OID; break; } if (bForwardRequest == FALSE) { if (Status == NDIS_STATUS_SUCCESS) { *BytesRead = InformationBufferLength; } } else { // // Send this request to the binding below. // Status = MPForwardRequest(pVElan, NdisRequestSetInformation, Oid, InformationBuffer, InformationBufferLength, BytesRead, BytesNeeded); } return(Status); } VOID MPReturnPacket( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet ) /*++ Routine Description: NDIS Miniport entry point called whenever protocols are done with a packet that we had indicated up and they had queued up for returning later. Arguments: MiniportAdapterContext - pointer to VELAN structure Packet - packet being returned. Return Value: None. --*/ { PVELAN pVElan = (PVELAN)MiniportAdapterContext; PNDIS_PACKET pOriginalPacket; PMUX_RECV_RSVD pRecvRsvd; #if IEEE_VLAN_SUPPORT NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo; #endif pRecvRsvd = MUX_RSVD_FROM_RECV_PACKET(Packet); pOriginalPacket = pRecvRsvd->pOriginalPacket; // // Reclaim our packet. // #if IEEE_VLAN_SUPPORT // // We would have set per-packet information if we had // extracted a tag header from the received packet. // NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET ( Packet, Ieee8021QInfo); // // If we did remove the tag header from the received packet, // we would have allocated a buffer to describe the "untagged" // header (see PtHandleRcvTagging); free it. // if (NdisPacket8021qInfo.Value) { NdisFreeBuffer(Packet->Private.Head); } #endif NdisFreePacket(Packet); // // Return the original packet received at our protocol // edge, if any. // // NOTE that we might end up calling NdisReturnPackets // multiple times with the same "lower" packet, based on // the number of VELANs to which we had indicated that // packet. The number of times we do so should match // the return value from our PtReceivePacket handler. // if (pOriginalPacket != NULL) { NdisReturnPackets(&pOriginalPacket, 1); } else { // // If no original packet, then we have been called // here to reclaim a packet used to forward up // a non-packet receive (see PtReceive). There // is nothing more to be done. // } MUX_DECR_PENDING_RECEIVES(pVElan); } NDIS_STATUS MPTransferData( OUT PNDIS_PACKET Packet, OUT PUINT BytesTransferred, IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_HANDLE MiniportReceiveContext, IN UINT ByteOffset, IN UINT BytesToTransfer ) /*++ Routine Description: Miniport's transfer data handler. This is called if we had indicated receive data using a non-packet API, for e.g. if the lookahead buffer did not contain the entire data. We need to forward this to the miniport below to that it can copy in the rest of the data. We call NdisTransferData to do so. However, when that completes (see PtTransferDataComplete), we have to get back at the VELAN from which this packet came so that we can complete this request with the right MiniportAdapterHandle. We therefore allocate a new packet, pointing to the same buffer as the packet just passed in, and use reserved space in the packet to hold a backpointer to the VELAN from which this came. Arguments: Packet Destination packet BytesTransferred Place to return how much data was copied MiniportAdapterContext Pointer to the VELAN structure MiniportReceiveContext Context ByteOffset Offset into the packet for copying data BytesToTransfer How much to copy. Return Value: Status of transfer --*/ { PVELAN pVElan = (PVELAN)MiniportAdapterContext; NDIS_STATUS Status; PNDIS_PACKET MyPacket; PMUX_TD_RSVD pTDReserved; #if IEEE_VLAN_SUPPORT PMUX_RCV_CONTEXT pMuxRcvContext; #endif do { NdisAllocatePacket(&Status, &MyPacket, pVElan->SendPacketPoolHandle); if (Status != NDIS_STATUS_SUCCESS) { break; } pTDReserved = MUX_RSVD_FROM_TD_PACKET(MyPacket); pTDReserved->pOriginalPacket = Packet; pTDReserved->pVElan = pVElan; MyPacket->Private.Flags = NdisGetPacketFlags(Packet) | MUX_SEND_PACKET_FLAGS; MyPacket->Private.Head = Packet->Private.Head; MyPacket->Private.Tail = Packet->Private.Tail; #ifdef WIN9X // // Work around the fact that NDIS does not initialize this // field on Win9x. // MyPacket->Private.ValidCounts = FALSE; #endif // WIN9X #if IEEE_VLAN_SUPPORT // // Check if the original received packet did contain a // VLAN tag header. If so, make sure we get the upcoming // call to NdisTransferData to skip the tag header. // pMuxRcvContext = (PMUX_RCV_CONTEXT)MiniportReceiveContext; if (pMuxRcvContext->TagHeaderLen == VLAN_TAG_HEADER_SIZE) { // // There was a tag header in the received packet. // ByteOffset += VLAN_TAG_HEADER_SIZE; // // Copy the 8021Q info into the packet // NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) = pMuxRcvContext->NdisPacket8021QInfo.Value; } // // Get back the lower driver's receive context for this indication. // MiniportReceiveContext = pMuxRcvContext->MacRcvContext; #endif NdisTransferData(&Status, pVElan->pAdapt->BindingHandle, MiniportReceiveContext, ByteOffset, BytesToTransfer, MyPacket, BytesTransferred); if (Status != NDIS_STATUS_PENDING) { PtTransferDataComplete(pVElan->pAdapt, MyPacket, Status, *BytesTransferred); Status = NDIS_STATUS_PENDING; } } while (FALSE); return(Status); } VOID MPHalt( IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: Halt handler. Add any further clean-up for the VELAN to this function. We wait for all pending I/O on the VELAN to complete and then unlink the VELAN from the adapter. Arguments: MiniportAdapterContext Pointer to the pVElan Return Value: None. --*/ { PVELAN pVElan = (PVELAN)MiniportAdapterContext; PADAPT pAdapt = pVElan->pAdapt; DBGPRINT(MUX_LOUD, ("==>MiniportHalt: VELAN %p\n", pVElan)); // // Mark the VELAN so that we don't send down any new requests or // sends to the adapter below, or new receives/indications to // protocols above. // pVElan->MiniportHalting = TRUE; // // Update the packet filter on the underlying adapter if needed. // if (pVElan->PacketFilter != 0) { MPSetPacketFilter(pVElan, 0); } // // Wait for any outstanding sends or requests to complete. // while (pVElan->OutstandingSends) { DBGPRINT(MUX_INFO, ("MiniportHalt: VELAN %p has %d outstanding sends\n", pVElan, pVElan->OutstandingSends)); NdisMSleep(20000); } // // Wait for all outstanding indications to be completed and // any pended receive packets to be returned to us. // while (pVElan->OutstandingReceives) { DBGPRINT(MUX_INFO, ("MiniportHalt: VELAN %p has %d outstanding receives\n", pVElan, pVElan->OutstandingReceives)); NdisMSleep(20000); } // // Delete the ioctl interface that was created when the miniport // was created. // (VOID)PtDeregisterDevice(); // // Unlink the VELAN from its parent ADAPT structure. This will // dereference the VELAN. // pVElan->MiniportAdapterHandle = NULL; PtUnlinkVElanFromAdapter(pVElan); DBGPRINT(MUX_LOUD, ("<== MiniportHalt: pVElan %p\n", pVElan)); } NDIS_STATUS MPForwardRequest( IN PVELAN pVElan, IN NDIS_REQUEST_TYPE RequestType, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesReadOrWritten, OUT PULONG BytesNeeded ) /*++ Routine Description: Utility routine that forwards an NDIS request made on a VELAN to the lower binding. Since at most a single request can be pended on a VELAN, we use the pre-allocated request structure embedded in the VELAN struct. Arguments: Return Value: NDIS_STATUS_PENDING if a request was sent down. --*/ { NDIS_STATUS Status; PMUX_NDIS_REQUEST pMuxNdisRequest = &pVElan->Request; DBGPRINT(MUX_LOUD, ("MPForwardRequest: VELAN %p, OID %x\n", pVElan, Oid)); do { MUX_INCR_PENDING_SENDS(pVElan); // // If the virtual miniport edge is at a low power // state, fail this request. // if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) { MUX_DECR_PENDING_SENDS(pVElan); Status = NDIS_STATUS_ADAPTER_NOT_READY; break; } pVElan->BytesNeeded = BytesNeeded; pVElan->BytesReadOrWritten = BytesReadOrWritten; pMuxNdisRequest->pCallback = PtCompleteForwardedRequest; switch (RequestType) { case NdisRequestQueryInformation: 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; break; case NdisRequestSetInformation: pMuxNdisRequest->Request.RequestType = NdisRequestSetInformation; pMuxNdisRequest->Request.DATA.SET_INFORMATION.Oid = Oid; pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer; pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength; break; default: ASSERT(FALSE); break; } // // If the lower binding has been notified of a low // power state, queue this request; it will be picked // up again when the lower binding returns to D0. // if (MUX_IS_LOW_POWER_STATE(pVElan->pAdapt->PtDevicePowerState)) { DBGPRINT(MUX_INFO, ("ForwardRequest: VELAN %p, Adapt %p power" " state is %d, queueing OID %x\n", pVElan, pVElan->pAdapt, pVElan->pAdapt->PtDevicePowerState, Oid)); pVElan->QueuedRequest = TRUE; Status = NDIS_STATUS_PENDING; break; } NdisRequest(&Status, pVElan->BindingHandle, &pMuxNdisRequest->Request); if (Status != NDIS_STATUS_PENDING) { PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status); Status = NDIS_STATUS_PENDING; break; } } while (FALSE); return (Status); } NDIS_STATUS MPSetPacketFilter( IN PVELAN pVElan, IN ULONG PacketFilter ) /*++ Routine Description: This routine will set up the VELAN so that it accepts packets that match the specified packet filter. The only filter bits that can truly be toggled are for broadcast and promiscuous. The MUX driver always sets the lower binding to promiscuous mode, but we do some optimization here to avoid turning on receives too soon. That is, we set the packet filter on the lower binding to a non-zero value iff at least one of the VELANs has a non-zero filter value. NOTE: setting the lower binding to promiscuous mode can impact CPU utilization. The only reason we set the lower binding to promiscuous mode in this sample is that we need to be able to receive unicast frames directed to MAC address(es) that do not match the local adapter's MAC address. If VELAN MAC addresses are set to be equal to that of the adapter below, it is sufficient to set the lower packet filter to the bitwise OR'ed value of packet filter settings on all VELANs. Arguments: pVElan - pointer to VELAN PacketFilter - the new packet filter Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_NOT_SUPPORTED --*/ { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PADAPT pAdapt; PVELAN pTmpVElan; PLIST_ENTRY p; ULONG AdapterFilter; BOOLEAN bSendUpdate = FALSE; LOCK_STATE LockState; DBGPRINT(MUX_LOUD, ("=> SetPacketFilter VELAN %p, Filter %x\n", pVElan, PacketFilter)); do { // // Any bits not supported? // if (PacketFilter & ~VELAN_SUPPORTED_FILTERS) { Status = NDIS_STATUS_NOT_SUPPORTED; break; } AdapterFilter = 0; pAdapt = pVElan->pAdapt; // // Grab a Write lock on the adapter so that this operation // does not interfere with any receives that might be accessing // filter information. // MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); // // Save the new packet filter value // pVElan->PacketFilter = PacketFilter; // // Compute the new combined filter for all VELANs on this // adapter. // for (p = pAdapt->VElanList.Flink; p != &pAdapt->VElanList; p = p->Flink) { pTmpVElan = CONTAINING_RECORD(p, VELAN, Link); AdapterFilter |= pTmpVElan->PacketFilter; } // // If all VELANs have packet filters set to 0, turn off // receives on the lower adapter, if not already done. // if ((AdapterFilter == 0) && (pAdapt->PacketFilter != 0)) { bSendUpdate = TRUE; pAdapt->PacketFilter = 0; } else // // If receives had been turned off on the lower adapter, and // the new filter is non-zero, turn on the lower adapter. // We set the adapter to promiscuous mode in this sample // so that we are able to receive packets directed to // any of the VELAN MAC addresses. // if ((AdapterFilter != 0) && (pAdapt->PacketFilter == 0)) { bSendUpdate = TRUE; pAdapt->PacketFilter = MUX_ADAPTER_PACKET_FILTER; } MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); if (bSendUpdate) { PtRequestAdapterAsync( pAdapt, NdisRequestSetInformation, OID_GEN_CURRENT_PACKET_FILTER, &pAdapt->PacketFilter, sizeof(pAdapt->PacketFilter), PtDiscardCompletedRequest); } break; } while (FALSE); DBGPRINT(MUX_INFO, ("<= SetPacketFilter VELAN %p, Status %x\n", pVElan, Status)); return(Status); } NDIS_STATUS MPSetMulticastList( IN PVELAN pVElan, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG pBytesRead, OUT PULONG pBytesNeeded ) /*++ Routine Description: Set the multicast list on the specified VELAN miniport. We simply validate all information and copy in the multicast list. We don't forward the multicast list information down since we set the lower binding to promisc. mode. Arguments: pVElan - VELAN on which to set the multicast list InformationBuffer - pointer to new multicast list InformationBufferLength - length in bytes of above list pBytesRead - place to return # of bytes read from the above pBytesNeeded - place to return expected min # of bytes Return Value: NDIS_STATUS --*/ { NDIS_STATUS Status; PADAPT pAdapt; LOCK_STATE LockState; // // Initialize. // *pBytesNeeded = sizeof(MUX_MAC_ADDRESS); *pBytesRead = 0; Status = NDIS_STATUS_SUCCESS; do { if (InformationBufferLength % sizeof(MUX_MAC_ADDRESS)) { Status = NDIS_STATUS_INVALID_LENGTH; break; } if (InformationBufferLength > (VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS))) { Status = NDIS_STATUS_MULTICAST_FULL; *pBytesNeeded = VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS); break; } pAdapt = pVElan->pAdapt; // // Grab a Write lock on the adapter so that this operation // does not interfere with any receives that might be accessing // multicast list information. // MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState); NdisZeroMemory(&pVElan->McastAddrs[0], VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS)); NdisMoveMemory(&pVElan->McastAddrs[0], InformationBuffer, InformationBufferLength); pVElan->McastAddrCount = InformationBufferLength / sizeof(MUX_MAC_ADDRESS); MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState); } while (FALSE); return (Status); } // // Careful! Uses static storage for string. Used to simplify DbgPrints // of MAC addresses. // PUCHAR MacAddrToString(PVOID In) { static UCHAR String[20]; static PUCHAR HexChars = "0123456789abcdef"; PUCHAR EthAddr = (PUCHAR) In; UINT i; PUCHAR s; for (i = 0, s = String; i < 6; i++, EthAddr++) { *s++ = HexChars[(*EthAddr) >> 4]; *s++ = HexChars[(*EthAddr) & 0xf]; } *s = '\0'; return String; } VOID MPGenerateMacAddr( PVELAN pVElan ) /*++ Routine Description: Generates a "virtual" MAC address for a VELAN. NOTE: this is only a sample implementation of selecting a MAC address for the VELAN. Other implementations are possible, including using the MAC address of the underlying adapter as the MAC address of the VELAN. Arguments: pVElan - Pointer to velan structure Return Value: None --*/ { pVElan->PermanentAddress[0] = 0x02 | (((UCHAR)pVElan->VElanNumber & 0x3f) << 2); pVElan->PermanentAddress[1] = 0x02 | (((UCHAR)pVElan->VElanNumber & 0x3f) << 3); ETH_COPY_NETWORK_ADDRESS( pVElan->CurrentAddress, pVElan->PermanentAddress); DBGPRINT(MUX_LOUD, ("%d CurrentAddress %s\n", pVElan->VElanNumber, MacAddrToString(&pVElan->CurrentAddress))); DBGPRINT(MUX_LOUD, ("%d PermanentAddress %s\n", pVElan->VElanNumber, MacAddrToString(&pVElan->PermanentAddress))); } #ifdef NDIS51_MINIPORT VOID MPCancelSendPackets( IN NDIS_HANDLE MiniportAdapterContext, IN PVOID CancelId ) /*++ Routine Description: The miniport entry point to handle cancellation of all send packets that match the given CancelId. If we have queued any packets that match this, then we should dequeue them and call NdisMSendComplete for all such packets, with a status of NDIS_STATUS_REQUEST_ABORTED. We should also call NdisCancelSendPackets in turn, on each lower binding that this adapter corresponds to. This is to let miniports below cancel any matching packets. Arguments: MiniportAdapterContext - pointer to VELAN structure CancelId - ID of packets to be cancelled. Return Value: None --*/ { PVELAN pVElan = (PVELAN)MiniportAdapterContext; // // If we queue packets on our VELAN/adapter structure, this would be // the place to acquire a spinlock to it, unlink any packets whose // Id matches CancelId, release the spinlock and call NdisMSendComplete // with NDIS_STATUS_REQUEST_ABORTED for all unlinked packets. // // // Next, pass this down so that we let the miniport(s) below cancel // any packets that they might have queued. // NdisCancelSendPackets(pVElan->pAdapt->BindingHandle, CancelId); return; } VOID MPDevicePnPEvent( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_DEVICE_PNP_EVENT DevicePnPEvent, IN PVOID InformationBuffer, IN ULONG InformationBufferLength ) /*++ Routine Description: This handler is called to notify us of PnP events directed to our miniport device object. Arguments: MiniportAdapterContext - pointer to VELAN structure DevicePnPEvent - the event InformationBuffer - Points to additional event-specific information InformationBufferLength - length of above Return Value: None --*/ { // TBD - add code/comments about processing this. return; } VOID MPAdapterShutdown( IN NDIS_HANDLE MiniportAdapterContext ) /*++ Routine Description: This handler is called to notify us of an impending system shutdown. Since this is not a hardware driver, there isn't anything specific we need to do about this. Arguments: MiniportAdapterContext - pointer to VELAN structure Return Value: None --*/ { return; } #endif // NDIS51_MINIPORT VOID MPUnload( IN PDRIVER_OBJECT DriverObject ) { NDIS_STATUS Status; DBGPRINT(MUX_LOUD, ("==> MPUnload: DriverObj %p\n", DriverObject)); NdisDeregisterProtocol(&Status, ProtHandle); DBGPRINT(MUX_LOUD, ("<== MPUnload \n")); } #if IEEE_VLAN_SUPPORT NDIS_STATUS MPHandleSendTagging( IN PVELAN pVElan, IN PNDIS_PACKET Packet, IN OUT PNDIS_PACKET MyPacket ) /*++ Routine Description: This function is called when the driver supports IEEE802Q tagging. It checks the packet to be sent on a VELAN and inserts a tag header if necessary. Arguments: PVELAN - pointer to VELAN structure Packet - pointer to original packet MyPacket - pointer to the new allocated packet Return Value: NDIS_STATUS_SUCCESS if the packet was successfully parsed and hence should be passed down to the lower driver. NDIS_STATUS_XXX otherwise. --*/ { NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo; PVOID pEthTagBuffer; PNDIS_BUFFER pNdisBuffer; PVOID pVa; ULONG BufferLength; PNDIS_BUFFER pFirstBuffer; PNDIS_BUFFER pSecondBuffer; NDIS_STATUS Status; NDIS_STATUS Status2; PVOID pStartVa; BOOLEAN IsFirstVa; PVLAN_TAG_HEADER pTagHeader; PUSHORT TypeLength; PUSHORT pTpid; ULONG BytesToSkip; PUSHORT pTypeLength; // // Add tag header here // Status = NDIS_STATUS_SUCCESS; NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET( MyPacket, Ieee8021QInfo); do { // // Insert a tag only if we have a configured VLAN ID // or there is non-zero VLAN/priority information to be // sent with the packet. // if ((pVElan->VlanId == 0) && (NdisPacket8021qInfo.Value == 0)) { // // No tag header needed. // break; } // // We don't support E-RIF // if (NdisPacket8021qInfo.TagHeader.CanonicalFormatId) { // // skip the packet, return NDIS_STATUS_FAILURE // Status = NDIS_STATUS_INVALID_PACKET; break; } // // The Vlan Id must be the same as the configured VLAN ID if it is non-zero // if ((NdisPacket8021qInfo.TagHeader.VlanId) && (pVElan->VlanId) && (NdisPacket8021qInfo.TagHeader.VlanId != pVElan->VlanId)) { Status = NDIS_STATUS_INVALID_PACKET; break; } // // Find the virtual address after the Ethernet Header // BytesToSkip = ETH_HEADER_SIZE; pNdisBuffer = Packet->Private.Head; IsFirstVa = TRUE; // // Assume the Ethernet Header is in the first buffer of the packet. // The following loop is to find the start address of the data after // the ethernet header. This may be either in the first NDIS buffer // or in the second. // while (TRUE) { #ifdef NDIS51_MINIPORT NdisQueryBufferSafe(pNdisBuffer, &pVa, &BufferLength, NormalPagePriority); #else NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength); #endif // // The query can fail if the system is low on resources. // if (pVa == NULL) { break; } // // Remember the start of the ethernet header for later. // if (IsFirstVa) { pStartVa = pVa; IsFirstVa = FALSE; } // // Have we gone far enough into the packet? // if (BytesToSkip == 0) { break; } // // Does the current buffer contain bytes past the Ethernet // header? If so, stop. // if (BufferLength > BytesToSkip) { pVa = (PVOID)((PUCHAR)pVa + BytesToSkip); BufferLength -= BytesToSkip; break; } // // We haven't gone past the Ethernet header yet, so go // to the next buffer. // BytesToSkip -= BufferLength; pNdisBuffer = NDIS_BUFFER_LINKAGE(pNdisBuffer); } if (pVa == NULL) { Status = NDIS_STATUS_RESOURCES; break; } // // Allocate space for the Ethernet + VLAN tag header. // pEthTagBuffer = NdisAllocateFromNPagedLookasideList(&pVElan->TagLookaside); // // Memory allocation failed, can't send out the packet // if (pEthTagBuffer == NULL) { Status = NDIS_STATUS_RESOURCES; break; } // // Allocate NDIS buffers for the Ethernet + VLAN tag header and // the data that follows these. // NdisAllocateBuffer(&Status, &pSecondBuffer, pVElan->BufferPoolHandle, pVa, // byte following the Eth+tag headers BufferLength); NdisAllocateBuffer(&Status2, &pFirstBuffer, pVElan->BufferPoolHandle, pEthTagBuffer, ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE); if (Status != NDIS_STATUS_SUCCESS || Status2 != NDIS_STATUS_SUCCESS) { // // One of the buffer allocations failed. // if (Status == NDIS_STATUS_SUCCESS) { NdisFreeBuffer(pSecondBuffer); } if (Status2 == NDIS_STATUS_SUCCESS) { NdisFreeBuffer(pFirstBuffer); } NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, pEthTagBuffer); Status = NDIS_STATUS_RESOURCES; break; } // // All allocations were successful, now prepare the packet // to be sent down to the lower driver. // MyPacket->Private.Head = NDIS_BUFFER_LINKAGE(pNdisBuffer); NdisChainBufferAtFront(MyPacket, pSecondBuffer) NdisChainBufferAtFront(MyPacket, pFirstBuffer) // // Prepare the Ethernet and tag headers. // NdisMoveMemory(pEthTagBuffer, pStartVa, 2 * ETH_LENGTH_OF_ADDRESS); pTpid = (PUSHORT)((PUCHAR)pEthTagBuffer + 2 * ETH_LENGTH_OF_ADDRESS); *pTpid = TPID; pTagHeader = (PVLAN_TAG_HEADER)(pTpid + 1); // // Write Ieee 802Q info to packet frame // INITIALIZE_TAG_HEADER_TO_ZERO(pTagHeader); if (NdisPacket8021qInfo.Value) { SET_USER_PRIORITY_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.UserPriority); } else { SET_USER_PRIORITY_TO_TAG(pTagHeader, 0); } SET_CANONICAL_FORMAT_ID_TO_TAG (pTagHeader, 0); if (NdisPacket8021qInfo.TagHeader.VlanId) { SET_VLAN_ID_TO_TAG (pTagHeader, NdisPacket8021qInfo.TagHeader.VlanId); } else { SET_VLAN_ID_TO_TAG (pTagHeader, pVElan->VlanId); } pTypeLength = (PUSHORT)((PUCHAR)pTagHeader + sizeof(pTagHeader->TagInfo)); *pTypeLength = *((PUSHORT)((PUCHAR)pStartVa + 2 * ETH_LENGTH_OF_ADDRESS)); // // Clear the Ieee8021QInfo field in packet being sent down // to prevent double tag insertion! // NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) = 0; break; } while (FALSE); return Status; } #endif // IEEE_VLAN_SUPPORT