diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/dlc/llcrcv.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/dlc/llcrcv.c')
-rw-r--r-- | private/ntos/dlc/llcrcv.c | 2079 |
1 files changed, 2079 insertions, 0 deletions
diff --git a/private/ntos/dlc/llcrcv.c b/private/ntos/dlc/llcrcv.c new file mode 100644 index 000000000..5a5f77b97 --- /dev/null +++ b/private/ntos/dlc/llcrcv.c @@ -0,0 +1,2079 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + llcrcv.c + +Abstract: + + The module implements the NDIS receive indication handling and + its routing to upper protocol modules or to link state machines. + + To understand the link related procedure of this module, you should read + Chapters 11 and 12 in IBM Token-Ring Architecture Reference. + + Contents: + LlcNdisReceiveIndication + LlcNdisReceiveComplete + ProcessType1_Frames + MakeRcvIndication + ProcessType2_Frames + ProcessNewSabme + LlcTransferData + LlcNdisTransferDataComplete + safe_memcpy + FramingDiscoveryCacheHit + +Author: + + Antti Saarenheimo (o-anttis) 18-MAY-1991 + +Revision History: + + 19-Nov-1992 rfirth + RtlMoveMemory on MIPS, copying from shared TR buffer fails (see rubric + for safe_memcpy). Changed to private memory mover for this particular + case + + 02-May-1994 rfirth + Added caching for auto-framing discovery (TEST/XID/SABME-UA) + +--*/ + +#include <dlc.h> +#include <llc.h> + +// +// private prototypes... +// + +VOID +safe_memcpy( + OUT PUCHAR Destination, + IN PUCHAR Source, + IN ULONG Length + ); + +BOOLEAN +FramingDiscoveryCacheHit( + IN PADAPTER_CONTEXT pAdapterContext, + IN PBINDING_CONTEXT pBindingContext + ); + +// +// Local lookup tables to receive the correct frames for a direct station +// + +static USHORT ReceiveMasks[LLC_PACKET_MAX] = { + DLC_RCV_8022_FRAMES, + DLC_RCV_MAC_FRAMES, + DLC_RCV_DIX_FRAMES, + DLC_RCV_OTHER_DESTINATION +}; + +static UCHAR FrameTypes[LLC_PACKET_MAX] = { + LLC_DIRECT_8022, + LLC_DIRECT_MAC, + LLC_DIRECT_ETHERNET_TYPE, + (UCHAR)(-1) +}; + +// +// functions +// + + +NDIS_STATUS +LlcNdisReceiveIndication( + IN PADAPTER_CONTEXT pAdapterContext, + IN NDIS_HANDLE MacReceiveContext, + IN PVOID pHeadBuf, + IN UINT cbHeadBuf, + IN PVOID pLookBuf, + IN UINT cbLookBuf, + IN UINT cbPacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link + endpoint (That was from SteveJ's NBF, the guy must have a degree in + the english literature). This routine is very time critical! + + We first check the frame type (token-ring, 802.3 ethernet or dix), + then check its data link address (802.2 saps or ethernet type) + and then we route it to the upper protocol that has opened the + address, that the frame was sent to. The link level frames + are first run through the protocol state machine, and only + the accepted I- frames are indicated to upper level. + +Arguments: + + pAdapterContext - The Adapter Binding specified at initialization time + + MacReceiveContext - Note: different from binding handle, mac needs this + to support re-entrant receive indications + + pHeadBuf - pointer to a buffer containing the packet header + + cbHeadBuf - size of the header + + pLookBuf - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at, not including header + + cbLookBuf - the size of the above. May be less than asked for, if + that's all there is + + cbPacketSize - Overall size of the packet, not including the header + +Assumes: + + pHeadBuf contains all the header information: + + 802.3 6 bytes destination address + 6 bytes source address + 2 bytes big-endian length or packet type (DIX frames) + + 802.5 1 byte Access Control + 1 byte Frame Control + 6 bytes destination address + 6 bytes source address + 0-18 bytes source routing + + FDDI 1 byte Frame Control + 6 bytes destination address + 6 bytes source address + + From this we can assume for Token Ring that if cbHeadBuf is >14 (decimal) + then there IS source routing information in the packet + +Return Value: + + NDIS_STATUS: + + NDIS_STATUS_SUCCESS + Packet accepted + + NDIS_STATUS_NOT_RECOGNIZED + Packet not recognized by protocol + + NDIS_any_other_thing if I understand, but can't handle. + +--*/ + +{ + LLC_HEADER llcHdr; + LAN802_ADDRESS Source; + LAN802_ADDRESS Destination; + USHORT EthernetTypeOrLength; + PDATA_LINK pLink; + PLLC_SAP pSap; + UCHAR PacketType = LLC_PACKET_8022; + UCHAR cbLanHeader = 14; + KIRQL OldIrql; + UCHAR packet[36]; // enough space for 14-byte header, 18-byte source + // routing, 1-byte DSAP, 1-byte SSAP & 2-byte LPDU + PLLC_OBJECT pObject; + + UNREFERENCED_PARAMETER(OldIrql); + + ASSUME_IRQL(DISPATCH_LEVEL); + + // + // we assume at least 13 bytes in the header for all media types. Also + // assume that the header is no larger than the packet buffer + // + + ASSERT(cbHeadBuf >= 13); + ASSERT(cbHeadBuf <= sizeof(packet)); + + if ( cbHeadBuf > LLC_MAX_LAN_HEADER ) { + return NDIS_STATUS_NOT_RECOGNIZED; + } + + LlcMemCpy(packet, pHeadBuf, cbHeadBuf); + LlcMemCpy(packet+cbHeadBuf, pLookBuf, sizeof(packet) - cbHeadBuf); + cbPacketSize += cbHeadBuf; + + // + // First we do the inital checking for the frame and read + // the destination and source address and LLC header to + // DWORD aligned addresses. We avoid any bigendiand/ + // small endiand problematic by forgotting the second high + // byte in the addresses. The lowest ULONG is used only as + // an raw data. The bytes can be accesses in any way. + // The macros read LLC header in a small endiand safe way. + // + + switch (pAdapterContext->NdisMedium) { + case NdisMedium802_3: + LlcMemCpy(Destination.Node.auchAddress, packet, 6); + LlcMemCpy(Source.Node.auchAddress, packet + 6, 6); + + // + // The 802.3 LLC frames have always the length field! + // A 802.3 MAC should discard all Ethernet frames + // longer than 1500 bytes. + // + // X'80D5 is a special ethernet type used when 802.2 frame + // is encapsulated inside a ethernet type header. + // (Ethernet type/size is in a reverse order for + // Intel architecture) + // + + EthernetTypeOrLength = (USHORT)packet[12] * 256 + (USHORT)packet[13]; + + if (EthernetTypeOrLength < 3) { + return NDIS_STATUS_INVALID_PACKET; + } + + // + // If the ethernet length/type field is more than 1500, the + // frame is dix frame and the length field is a dix ethernet + // address. Otherwise the frame is a normal 802.3 frame, + // that has always LLC header inside it. + // + + if (EthernetTypeOrLength > 1500) { + if (EthernetTypeOrLength == 0x80D5) { + + // + // This is a special 'IBM SNA over ethernet' type, + // that consists of the length field, 1 byte padding + // and complete 802.2 LLC header (including the info field). + // + + cbLanHeader = 17; + (PUCHAR)pLookBuf += 3; + cbLookBuf -= 3; + + // + // The DIX frame size is stored as a big-endian USHORT at offset + // 15 in the LAN header. Add 17 for the DIX LAN header: + // + // 6 bytes destination address + // 6 bytes source address + // 2 bytes DIX identifier (0x80D5) + // 2 byte big-endian information frame length + // 1 byte pad + // + + pAdapterContext->cbPacketSize = (USHORT)packet[14] * 256 + + (USHORT)packet[15] + + 17; + + // + // we now keep an indicator which explicitly defines that this + // frame has (SNA) DIX framing + // + + pAdapterContext->IsSnaDixFrame = TRUE; + } else { + + // + // This is some other DIX format frame. We don't know what the + // format of this is (app-specific). We hand the entire packet + // to the app and let it sort out the format. The frame may be + // padded in which case the app gets the padding too + // + + // + // This is still Ethernet, so cbHeadBuf is 14, even though + // the actual LAN header is only 12 + // + + PacketType = LLC_PACKET_DIX; + pAdapterContext->cbPacketSize = cbPacketSize; + + // + // this frame is not SNA DIX, although it is generically a DIX + // frame. It will be indicated via a specific DIX SAP, not as + // a general ethernet frame + // + + pAdapterContext->IsSnaDixFrame = FALSE; + } + } else { + + // + // Ethernet packets include always the padding, + // we use the actual size saved in 802.3 header. + // Include also the header: 6 + 6 + 2 + // + + pAdapterContext->cbPacketSize = EthernetTypeOrLength + 14; + + // + // this is an 802.3 frame - not DIX at all + // + + pAdapterContext->IsSnaDixFrame = FALSE; + } + break; + + case NdisMedium802_5: + LlcMemCpy(Destination.Node.auchAddress, packet + 2, 6); + LlcMemCpy(Source.Node.auchAddress, packet + 8, 6); + + // + // cbHeadBuf always has the correct LAN header length for Token Ring + // + + cbLanHeader = cbHeadBuf; + + pAdapterContext->cbPacketSize = cbPacketSize; + + // + // bit7 and bit6 in FC byte defines the frame type in token ring. + // 00 => MAC frame (no LLC), 01 => LLC, 10,11 => reserved. + // We send all other frames to direct except 01 (LLC) + // + + if ((packet[1] & 0xC0) == 0x40) { + + // + // check if we have routing info? + // + + if (Source.Node.auchAddress[0] & 0x80) { + + // + // reset the source routing indicator in the + // source address (it would screw up the link search) + // + + Source.Node.auchAddress[0] &= 0x7f; + + // + // Discard all invalid TR frames, they'd corrupt the memory + // + + if (cbLanHeader > MAX_TR_LAN_HEADER_SIZE) { + return NDIS_STATUS_NOT_RECOGNIZED; + } + } + } else { + + // + // this is a MAC frame destined to direct station + // + + PacketType = LLC_PACKET_MAC; + } + break; + + case NdisMediumFddi: + LlcMemCpy(Destination.Node.auchAddress, packet + 1, 6); + LlcMemCpy(Source.Node.auchAddress, packet + 7, 6); + + // + // cbHeadBuf always has the correct LAN header length for FDDI + // + + cbLanHeader = cbHeadBuf; + + pAdapterContext->cbPacketSize = cbPacketSize; + + // + // bit5 and bit4 in FC byte define the FDDI frame type: + // + // 00 => MAC or SMT + // 01 => LLC + // 10 => implementer (?) + // 11 => reserved + // + // do same as TR: LLC frames to link/SAP, everything else to direct + // station + // + + if ((packet[0] & 0x30) != 0x10) { + PacketType = LLC_PACKET_MAC; + } + break; + +#if LLC_DBG + default: + LlcInvalidObjectType(); + break; +#endif + + } + + pAdapterContext->FrameType = FrameTypes[PacketType]; + + // + // Direct interface gets all non LLC frames and also all LLC frames + // that were not sent to this station (ie. different destination + // address field and having no broadcast bit (bit7) set in + // destination address)). Ie. promiscuous mode, this data link + // version does not support promiscuous mode. + // + + if (Destination.Node.auchAddress[0] & pAdapterContext->IsBroadcast) { + pAdapterContext->ulBroadcastAddress = Destination.Address.ulLow; + pAdapterContext->usBroadcastAddress = Destination.Address.usHigh; + } else { + pAdapterContext->ulBroadcastAddress = 0; + + // + // We must also be able to handle the promiscuous packets + // + + if (Destination.Address.ulLow != pAdapterContext->Adapter.Address.ulLow + && Destination.Address.usHigh != pAdapterContext->Adapter.Address.usHigh) { + PacketType = LLC_PACKET_OTHER_DESTINATION; + } + } + + // + // Setup the current receive indication context, + // there can be only one simultaneous receive indication from + // a network adapter simultaneously. We save the necessary + // data into adapter context to save unnecessary stack operations + // + + pAdapterContext->NdisRcvStatus = NDIS_STATUS_NOT_RECOGNIZED; + pAdapterContext->LinkRcvStatus = STATUS_SUCCESS; + pAdapterContext->MacReceiveContext = MacReceiveContext; + pAdapterContext->pHeadBuf = (PUCHAR)pHeadBuf; + pAdapterContext->cbHeadBuf = cbHeadBuf; + pAdapterContext->pLookBuf = (PUCHAR)pLookBuf; + pAdapterContext->cbLookBuf = cbLookBuf; + pAdapterContext->RcvLanHeaderLength = (USHORT)cbLanHeader; + + ACQUIRE_DRIVER_LOCK(); + + ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); + + switch(PacketType) { + case LLC_PACKET_8022: + + // + // Read the whole LLC frame (a maybe an extra byte, + // if this is a U frame). + // Note: Source and destination saps are swapped in + // the received frames + // + + Source.Address.SrcSap = llcHdr.S.Dsap = packet[cbLanHeader]; + llcHdr.S.Ssap = packet[cbLanHeader + 1]; + Source.Address.DestSap = llcHdr.S.Ssap & (UCHAR)0xfe; + llcHdr.S.Command = packet[cbLanHeader + 2]; + llcHdr.S.Nr = packet[cbLanHeader + 3]; + + if (pSap = pAdapterContext->apSapBindings[llcHdr.U.Dsap]) { + + // + // The broadcast addresses cannot be destined to link stations + // + + if (pAdapterContext->ulBroadcastAddress == 0) { + SEARCH_LINK(pAdapterContext, Source, pLink); + if (pLink) { + + // + // Process all connection oriented frames, the procedure + // will call ProcessType1_Frames, if it finds that the + // frame is connectionless. + // (We should bring the whole subprocedure here, + // because it isn't called elsewhere). + // + + ProcessType2_Frames(pAdapterContext, pLink, llcHdr); + } else { + + // + // Process all connectionless frames and + // SABMEs (connection requests to create a + // new link station) + // + + ProcessType1_Frames(pAdapterContext, pSap, llcHdr); + } + } else { + + // + // Process the broadcasts, this cannot have + // nothing to do with the links + // + + ProcessType1_Frames(pAdapterContext, pSap, llcHdr); + } + } else { + + // + // The SAP has not been defined, but we must still respond + // to the TEST and XID commands sent to the NULL SAP. + // They must be echoed back to the sender + // + + if ((llcHdr.U.Dsap == LLC_SSAP_NULL) + && !(llcHdr.U.Ssap & LLC_SSAP_RESPONSE)) { + + // + // if the remote machine is already in the framing discovery + // cache but is using the other framing type then discard this + // TEST/XID command/response + // + +// if (FramingDiscoveryCacheHit(pAdapterContext, pSap->Gen.pLlcBinding)) { +// break; +// } + + RespondTestOrXid(pAdapterContext, llcHdr, LLC_SSAP_NULL); + } else if (pAdapterContext->pDirectStation != NULL) { + pAdapterContext->usRcvMask = ReceiveMasks[PacketType]; + MakeRcvIndication(pAdapterContext, (PLLC_OBJECT)pAdapterContext->pDirectStation); + } + } + break; + + case LLC_PACKET_DIX: + + // + // Search the DIX packet from the database + // + + pObject = (PLLC_OBJECT)pAdapterContext->aDixStations[EthernetTypeOrLength % MAX_DIX_TABLE]; + if (pObject) { + pAdapterContext->EthernetType = EthernetTypeOrLength; + } else { + pObject = (PLLC_OBJECT)pAdapterContext->pDirectStation; + if (pObject) { + pAdapterContext->usRcvMask = ReceiveMasks[PacketType]; + } + } + if (pObject) { + MakeRcvIndication(pAdapterContext, pObject); + } + break; + + case LLC_PACKET_OTHER_DESTINATION: + case LLC_PACKET_MAC: + + // + // discard the return status of the direct stations! + // The combining of the returns statuses would take too much time + // NDIS 3.0 isn't actually any more intrested if frame is copied. + // + + if (pObject = (PLLC_OBJECT)pAdapterContext->pDirectStation) { + pAdapterContext->usRcvMask = ReceiveMasks[PacketType]; + MakeRcvIndication(pAdapterContext, pObject); + } + break; + +#if LLC_DBG + default: + LlcInvalidObjectType(); + break; +#endif + + } + + RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); + + RELEASE_DRIVER_LOCK(); + + return pAdapterContext->NdisRcvStatus; +} + + +VOID +LlcNdisReceiveComplete( + IN PADAPTER_CONTEXT pAdapterContext + ) + +/*++ + +Routine Description: + + The routine handles the receive complete indications. The receive + completion is made by NDIS when the network hardware have been + enabled again for receive. In a UP Nt this does mean, that a + new frame could be received, because we are still on DPC level and + the receive indication is still in DPC queue to wait us to complete. + Actually that is OK, because otherwise the stack would overflow, + if there would be too many received packets. + +Arguments: + + pAdapterContext - adapter context + +Return Value: + + None + +--*/ + +{ + // + // seems that 3Com FDDI card is calling this at PASSIVE_LEVEL + // + + ASSUME_IRQL(ANY_IRQL); + + ACQUIRE_DRIVER_LOCK(); + + // + // Skip the whole background process if there is nothing to do. + // its the default case, when we are receiving I or UI data. + // + + if (pAdapterContext->LlcPacketInSendQueue + || !IsListEmpty(&pAdapterContext->QueueCommands) + || !IsListEmpty(&pAdapterContext->QueueEvents)) { + + ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + BackgroundProcessAndUnlock(pAdapterContext); + } + + RELEASE_DRIVER_LOCK(); +} + + +VOID +ProcessType1_Frames( + IN PADAPTER_CONTEXT pAdapterContext, + IN PLLC_SAP pSap, + IN LLC_HEADER LlcHeader + ) + +/*++ + +Routine Description: + + Route UI, TEST or XID frames to the LLC client + +Arguments: + + pAdapterContext - The Adapter Binding specified at initialization time. + pSap - pointer to the SAP object of data link driver + LlcHeader - 802.2 header is copied to stack to make its access fast + +Return Value: + + None. + +--*/ + +{ + UCHAR DlcCommand; + + ASSUME_IRQL(DISPATCH_LEVEL); + + // + // Update the counter, we must later check the lost frames + // (no buffers available for the received UI- frames) + // + + pSap->Statistics.FramesReceived++; + + // + // We must use the link station state machine with any other + // command except UI frames and broadcasts, if the link exists. + // + + if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_UI) { + pAdapterContext->FrameType = LLC_UI_FRAME; + MakeRcvIndication(pAdapterContext, (PLLC_OBJECT)pSap); + return; + + // + // Check next if the frame is a XID or TEST frame + // + + } else if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_TEST) { + + // + // if the remote machine is already in the framing discovery cache but + // is using the other framing type then discard this TEST command/response + // + + // + // RLF 06/23/94 + // + // If this is a Response from SAP 0 then don't check the cache. The + // reason is that currently DLC will automatically generate responses + // to TESTs and XIDs sent to SAP 0. It will generate 802.3 and DIX + // irrespective of whether it is configured for DIX or not. The upshot + // is that a DIX-only machine can currently send an 802.3 response + // which when we run it through the cache, causes us to assume the other + // machine is configured for 802.3, not DIX. In communicado. + // For TEST and XIDs from SAP 0, we have to let the app receive the + // duplicate and decide what to do with it + // + + if (LlcHeader.U.Ssap != (LLC_SSAP_NULL | LLC_SSAP_RESPONSE)) { + if (FramingDiscoveryCacheHit(pAdapterContext, pSap->Gen.pLlcBinding)) { + return; + } + } + if (!(LlcHeader.U.Ssap & LLC_SSAP_RESPONSE)) { + + // + // The Test commands are always echoed back + // (the Command/Response bit was reset => this is command) + // + + RespondTestOrXid(pAdapterContext, LlcHeader, pSap->SourceSap); + pAdapterContext->NdisRcvStatus = NDIS_STATUS_SUCCESS; + return; + } else { + DlcCommand = LLC_TEST_RESPONSE_NOT_FINAL; + } + } else if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_XID) { + + // + // if the remote machine is already in the framing discovery cache but + // is using the other framing type then discard this XID command/response + // + + // + // RLF 06/23/94 + // + // If this is a Response from SAP 0 then don't check the cache. See above + // + + if (LlcHeader.U.Ssap != (LLC_SSAP_NULL | LLC_SSAP_RESPONSE)) { + if (FramingDiscoveryCacheHit(pAdapterContext, pSap->Gen.pLlcBinding)) { + return; + } + } + + // + // The upper level protocol may ask data link driver to handle the XIDs + // + + if (!(LlcHeader.U.Ssap & LLC_SSAP_RESPONSE)) { + if (pSap->OpenOptions & LLC_HANDLE_XID_COMMANDS) { + RespondTestOrXid(pAdapterContext, LlcHeader, pSap->SourceSap); + pAdapterContext->NdisRcvStatus = NDIS_STATUS_SUCCESS; + return; + } else { + DlcCommand = LLC_XID_COMMAND_NOT_POLL; + } + } else { + DlcCommand = LLC_XID_RESPONSE_NOT_FINAL; + } + } else if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_SABME) { + + // + // can't open a connection by broadcasting a SABME + // + + if (pAdapterContext->ulBroadcastAddress != 0) { + return; + } + + // + // if the remote machine is already in the framing discovery cache but + // is using the other framing type then discard this SABME + // + + if (FramingDiscoveryCacheHit(pAdapterContext, pSap->Gen.pLlcBinding)) { + return; + } + + // + // This is a remote connection request + // + + ProcessNewSabme(pAdapterContext, pSap, LlcHeader); + pAdapterContext->NdisRcvStatus = NDIS_STATUS_SUCCESS; + return; + } else { + return; + } + + if (LlcHeader.auchRawBytes[2] & LLC_U_POLL_FINAL) { + DlcCommand -= 2; + } + + pAdapterContext->FrameType = DlcCommand; + MakeRcvIndication(pAdapterContext, (PLLC_OBJECT)pSap); +} + + +VOID +MakeRcvIndication( + IN PADAPTER_CONTEXT pAdapterContext, + IN PLLC_OBJECT pStation + ) + +/*++ + +Routine Description: + + Procedure makes a generic receive indication for all frames + received by SAP or direct stations. + +Arguments: + + pAdapterContext - adapter context of the received packet + pStation - SAP or DIRECT station + +Return Value: + + None. + +--*/ + +{ + ASSUME_IRQL(DISPATCH_LEVEL); + + // + // SAP and direct stations can be shared by several link clients + // (if they have been opened in shared mode). Route the packet + // to all clints registered to this SAP or direct station + // A link station may have only one owner. + // + + for (; pStation; pStation = (PLLC_OBJECT)pStation->Gen.pNext) { + + // + // Rotate the direct frames to all direct stations except if + // the frame has already been captured by the current client. + // We use 32-bit client context to identify the client, + // that had already received the frame to its SAP or link station. + // + // Broadcasts are indicated only when they match with the + // group or functional address defined for this binding. + // The global broadcast is passed through, if it is enabled. + // + + if ( + + // + // 1. Check is this is destinated frame (broadcast is null) or + // if the packet is a broadcast with a matching group address + // + + ((pAdapterContext->ulBroadcastAddress == 0) + || (pAdapterContext->ulBroadcastAddress == 0xFFFFFFFFL) + || ((pAdapterContext->ulBroadcastAddress & pStation->Gen.pLlcBinding->Functional.ulAddress) + && ((pAdapterContext->ulBroadcastAddress & pStation->Gen.pLlcBinding->ulFunctionalZeroBits) == 0) + && (pAdapterContext->usBroadcastAddress == pAdapterContext->usHighFunctionalBits)) + || ((pAdapterContext->ulBroadcastAddress == pStation->Gen.pLlcBinding->ulBroadcastAddress) + && (pAdapterContext->usBroadcastAddress == pStation->Gen.pLlcBinding->usBroadcastAddress))) + + // + // 2. If the station type is DIX, then the ethernet type + // must be the same as the station's ethernet type + + && ((pStation->Gen.ObjectType != LLC_DIX_OBJECT) + || (pStation->Dix.ObjectAddress == pAdapterContext->EthernetType)) + + // + // 3. If the packet is a direct frame, then its receive mask + // must match with the received frame. + // + + && ((pStation->Gen.ObjectType != LLC_DIRECT_OBJECT) + || (pStation->Dir.OpenOptions & pAdapterContext->usRcvMask))) { + + UINT Status; + + // + // Update the counter, we must later check the lost frames + // (if no buffers available for the received frames) + // + + pStation->Sap.Statistics.FramesReceived++; + pAdapterContext->NdisRcvStatus = NDIS_STATUS_SUCCESS; + Status = pStation->Gen.pLlcBinding->pfReceiveIndication( + pStation->Gen.pLlcBinding->hClientContext, + pStation->Gen.hClientHandle, + pAdapterContext->FrameType, + pAdapterContext->pLookBuf, + pAdapterContext->cbPacketSize - pAdapterContext->RcvLanHeaderLength + ); + + // + // Protocol may discard the packet and its indication. + // + + if (Status != STATUS_SUCCESS) { + pStation->Sap.Statistics.DataLostCounter++; + if (Status == DLC_STATUS_NO_RECEIVE_COMMAND) { + pStation->Sap.Statistics.FramesDiscardedNoRcv++; + } + } + } + } +} + + +VOID +ProcessType2_Frames( + IN PADAPTER_CONTEXT pAdapterContext, + IN OUT PDATA_LINK pLink, + IN LLC_HEADER LlcHeader + ) + +/*++ + +Routine Description: + + Procedure preprocess LLC Type2 frames for the actual state machine. + Type 2 LLC frames are: I, RR, RNR, REJ, SABME, DISC, UA, DM, FRMR. + The data is indicated to the upper protocol module, if it sequence + number of the I- frame is valid, but the receive may still fail, + if the data packet is discarded by the 802.2 state machine. + The data is first indicated to the client, because we must set + first the state machine to the local busy state, if the upper + protocol module has not enough buffers to receive the data. + +Arguments: + + pAdapterContext - The Adapter Binding specified at initialization time. + pLink - link station data + LlcHeader - LLC header + +Return Value: + + None. + +--*/ + +{ + UCHAR uchInput; + BOOLEAN boolPollFinal; + UINT status; + + ASSUME_IRQL(DISPATCH_LEVEL); + + // + // The last received command is included in the DLC statistics + // + + pLink->LastCmdOrRespReceived = LlcHeader.U.Command; + + // + // Handle first I frames, they are the most common! + // + + if (!(LlcHeader.U.Command & LLC_NOT_I_FRAME)) { + + // + // Check first the sync of the I- frame: The send sequence + // number should be what we are expected or some packets sare lost. + // + + uchInput = IS_I_r0; // In Sequence Information frame by default + + // + // we discard all I-frames, that are bigger than the + // maximum defined for this link station. + // This must be the best way to solve wrong packet size. + // FRMR disconnects the packets and the invalid transmit + // command should fail in the sending side. + // + + pLink->Nr = LlcHeader.I.Nr & (UCHAR)0xfe; + if (pLink->MaxIField + pAdapterContext->RcvLanHeaderLength + + sizeof(LLC_HEADER) < pAdapterContext->cbPacketSize) { + uchInput = LPDU_INVALID_r0; + } else if ((LlcHeader.I.Ns & (UCHAR)0xfe) != pLink->Vr) { + + // + // Out of Sequence Information frame (we didn't expect this!) + // + + uchInput = OS_I_r0; + + // + // When we are out of receive buffers, we want to know + // the buffer space required by all expected frames. + // There may be several coming I-frames in the send queues, + // bridges and in the receive buffers of the adapter when a + // link enters a local busy state. We save the size of + // all received sequential I-frames during a local busy state + // to know how much buffer space we must commit before we + // can clear the local busy state. + // + + if ((pLink->Flags & DLC_LOCAL_BUSY_BUFFER) + && (LlcHeader.I.Ns & (UCHAR)0xfe) == pLink->VrDuringLocalBusy) { + pLink->VrDuringLocalBusy += 2; + pLink->BufferCommitment += BufGetPacketSize(pAdapterContext->cbPacketSize); + } + + // + // The valid frames has modulo: Va <= Nr <= Vs, + // Ie. the Receive sequence number should belong to + // a frame that has been sent but not acknowledged. + // The extra check in the beginning makes the most common + // code path faster: usually the other is waiting the next frame. + // + + } else if (pLink->Nr != pLink->Vs) { + + // + // There may by something wrong with the receive sequence number + // + + if (pLink->Vs >= pLink->Va) { + if (pLink->Nr < pLink->Va || pLink->Nr > pLink->Vs) { + uchInput = LPDU_INVALID_r0; + } + } else { + if (pLink->Nr > pLink->Vs && pLink->Nr < pLink->Va) { + uchInput = LPDU_INVALID_r0; + } + } + } + + // + // We must first indcate the frame to the upper protocol and + // then check, if it was accepted by the state machine. + // If a I- frame cannot be received by the upper protocol + // driver, then it must be dropped to the floor and be not + // indicated to the state machine (=> the frame will be lost + // for the LLC protocol) + // + + // + // RLF 04/13/93 + // + // if the link is in local busy (user) state then don't indicate the + // frame, but RNR it + // + + if ((uchInput == IS_I_r0) && !(pLink->Flags & DLC_LOCAL_BUSY_USER)) { + + DLC_STATUS Status; + + pAdapterContext->LinkRcvStatus = STATUS_PENDING; + Status = pLink->Gen.pLlcBinding->pfReceiveIndication( + pLink->Gen.pLlcBinding->hClientContext, + pLink->Gen.hClientHandle, + LLC_I_FRAME, + pAdapterContext->pLookBuf, + pAdapterContext->cbPacketSize - pAdapterContext->RcvLanHeaderLength + ); + + // + // We use local busy to stop the send to the link. + // IBM link station flow control management supports + // local busy state enabling because of "out of receive buffers" + // or "no outstanding receive". + // + + if (Status != STATUS_SUCCESS) { + if (Status == DLC_STATUS_NO_RECEIVE_COMMAND + || Status == DLC_STATUS_OUT_OF_RCV_BUFFERS) { + + ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + // + // We will enter to a local busy state because of + // out of buffers. Save the buffer size required + // to receive this data. + // + + pLink->VrDuringLocalBusy = pLink->Vr; + pLink->BufferCommitment = BufGetPacketSize(pAdapterContext->cbPacketSize); + + // + // We do not need to care, if the local busy state + // is already set or not. The state machine just + // returns an error status, but we do not care + // about it. The dlc status code trigger indication + // to the upper levels, if the state machine accepted + // the command. + // + + pLink->Flags |= DLC_LOCAL_BUSY_BUFFER; + pLink->DlcStatus.StatusCode |= INDICATE_LOCAL_STATION_BUSY; + RunStateMachineCommand(pLink, ENTER_LCL_Busy); + + RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + } + } + } + + ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + // + // The most common case is handled as a special case. + // We can save maybe 30 instrunctions. + // + + if (uchInput == IS_I_r0 && pLink->State == LINK_OPENED) { + UpdateVa(pLink); + pLink->Vr += 2; + pAdapterContext->LinkRcvStatus = STATUS_SUCCESS; + + // + // IS_I_c1 = Update_Va; Rcv_BTU; [Send_ACK] + // IS_I_r|IS_I_c0 = Update_Va; Rcv_BTU; TT2; Ir_Ct=N3; [RR_r](1) + // + + if ((LlcHeader.I.Nr & LLC_I_S_POLL_FINAL) + && !(LlcHeader.I.Ssap & LLC_SSAP_RESPONSE)) { + StopTimer(&pLink->T2); + pLink->Ir_Ct = pLink->N3; + SendLlcFrame(pLink, (UCHAR)(DLC_RR_TOKEN | DLC_TOKEN_RESPONSE | 1)); + } else { + SendAck(pLink); + } + } else { + uchInput += (UINT)(LlcHeader.I.Nr & LLC_I_S_POLL_FINAL); + + if (!(LlcHeader.I.Ssap & LLC_SSAP_RESPONSE)) { + uchInput += DLC_TOKEN_COMMAND; + } + + // + // Nr will be some garbage in the case of U commands, + // but the Poll/Final flag is not used when the U- commands + // are processed. + // ---- + // If the state machine returns an error to link receive status, + // then the receive command completion cancels the received + // frame. + // + + pAdapterContext->LinkRcvStatus = RunStateMachine( + pLink, + (USHORT)uchInput, + (BOOLEAN)((LlcHeader.S.Nr & LLC_I_S_POLL_FINAL) ? 1 : 0), + (BOOLEAN)(LlcHeader.S.Ssap & LLC_SSAP_RESPONSE) + ); + } + + RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + // + // Update the error counters if something went wrong with + // the receive. + // + + if (pAdapterContext->LinkRcvStatus != STATUS_SUCCESS) { + + // + // We will count all I frames not actually acknowledged + // as errors (this could be counted also other way). + // + + pLink->Statistics.I_FrameReceiveErrors++; + if (pLink->Statistics.I_FrameReceiveErrors == 0x80) { + pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW; + } + } else { + + // + // update statistics: in-sequency frames OK, all others + // must be errors. + // This may not be the best place to count successful I-frames, + // because the state machine has not yet acknowledged this frame, + // We may be in a wrong state to receive any data (eg. local busy) + // + + pLink->Statistics.I_FramesReceived++; + if (pLink->Statistics.I_FramesReceived == 0x8000) { + pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW; + } + pLink->pSap->Statistics.FramesReceived++; + } + + // + // We may complete this only if the transfer data has + // already completed (and there is a receive completion + // packet built up in). + // + + if (pLink->Gen.pLlcBinding->TransferDataPacket.pPacket != NULL + && pLink->Gen.pLlcBinding->TransferDataPacket.pPacket->Data.Completion.Status != NDIS_STATUS_PENDING) { + + // + // The NDIS status is saved in the completion status, we + // will use state machine status instead, if the state + // machine returned an error. + // + + if (pAdapterContext->LinkRcvStatus != STATUS_SUCCESS) { + pLink->Gen.pLlcBinding->TransferDataPacket.pPacket->Data.Completion.Status = pAdapterContext->LinkRcvStatus; + } + pLink->Gen.pLlcBinding->pfCommandComplete( + pLink->Gen.pLlcBinding->hClientContext, + pLink->Gen.pLlcBinding->TransferDataPacket.pPacket->Data.Completion.hClientHandle, + pLink->Gen.pLlcBinding->TransferDataPacket.pPacket + ); + pLink->Gen.pLlcBinding->TransferDataPacket.pPacket = NULL; + } + + // + // ******** EXIT *********** + // + + return; + } else if (!(LlcHeader.S.Command & LLC_U_TYPE_BIT)) { + + // + // Handle S (Supervisory) commands (RR, REJ, RNR) + // + + switch (LlcHeader.S.Command) { + case LLC_RR: + uchInput = RR_r0; + break; + + case LLC_RNR: + uchInput = RNR_r0; + break; + + case LLC_REJ: + uchInput = REJ_r0; + break; + + default: + uchInput = LPDU_INVALID_r0; + break; + } + + // + // The valid frames has modulo: Va <= Nr <= Vs, + // Ie. the Receive sequence number should belong to + // a frame that has been sent but not acknowledged. + // The extra check in the beginning makes the most common + // code path faster: usually the other is waiting the next frame. + // (keep the rest code the same as in I path, even a very + // primitive optimizer will puts these code paths together) + // + + pLink->Nr = LlcHeader.I.Nr & (UCHAR)0xfe; + if (pLink->Nr != pLink->Vs) { + + // + // Check the received sequence number + // + + if (pLink->Vs >= pLink->Va) { + if (pLink->Nr < pLink->Va || pLink->Nr > pLink->Vs) { + uchInput = LPDU_INVALID_r0; + } + } else { + if (pLink->Nr > pLink->Vs && pLink->Nr < pLink->Va) { + uchInput = LPDU_INVALID_r0; + } + } + } + uchInput += (UINT)(LlcHeader.S.Nr & LLC_I_S_POLL_FINAL); + boolPollFinal = (BOOLEAN)(LlcHeader.S.Nr & LLC_I_S_POLL_FINAL); + + if (!(LlcHeader.S.Ssap & LLC_SSAP_RESPONSE)) { + uchInput += DLC_TOKEN_COMMAND; + } + } else { + + // + // Handle U (Unnumbered) command frames + // (FRMR, DM, UA, DISC, SABME, XID, TEST) + // + + switch (LlcHeader.U.Command & ~LLC_U_POLL_FINAL) { + case LLC_DISC: + uchInput = DISC0; + break; + + case LLC_SABME: + uchInput = SABME0; + break; + + case LLC_DM: + uchInput = DM0; + break; + + case LLC_UA: + uchInput = UA0; + break; + + case LLC_FRMR: + uchInput = FRMR0; + break; + + default: + + // + // we don't handle XID and TEST frames here! + // + + ProcessType1_Frames(pAdapterContext, pLink->pSap, LlcHeader); + return; + break; + }; + + // + // We set an uniform poll/final bit for procedure call + // + + boolPollFinal = FALSE; + if (LlcHeader.U.Command & LLC_U_POLL_FINAL) { + uchInput += 1; + boolPollFinal = TRUE; + } + } + + ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + // + // Note: the 3rd parameter must be 0 or 1, fortunately the + // the poll/final bit is bit0 in S and I frames. + // + + status = RunStateMachine(pLink, + (USHORT)uchInput, + boolPollFinal, + (BOOLEAN)(LlcHeader.S.Ssap & LLC_SSAP_RESPONSE) + ); + + // + // if this frame is a UA AND it was accepted by the FSM AND the framing type + // is currently unspecified then set it to the type in the UA frame received. + // If this is not an ethernet adapter or we are not in AUTO mode then the + // framing type for this link is set to the framing type in the binding + // context (as it was before) + // + + if ((status == STATUS_SUCCESS) + && (uchInput == UA0) + && (pLink->FramingType == LLC_SEND_UNSPECIFIED)) { + + // + // RLF 05/09/94 + // + // If we received a UA in response to a SABME that we sent out as DIX + // and 802.3, then record the framing type. This will be used for all + // subsequent frames sent on this link + // + + pLink->FramingType = (IS_SNA_DIX_FRAME(pAdapterContext) + && IS_AUTO_BINDING(pLink->Gen.pLlcBinding)) + ? LLC_SEND_802_3_TO_DIX + : pLink->Gen.pLlcBinding->InternalAddressTranslation + ; + } + + RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock); +} + + +VOID +ProcessNewSabme( + IN PADAPTER_CONTEXT pAdapterContext, + IN PLLC_SAP pSap, + IN LLC_HEADER LlcHeader + ) + +/*++ + +Routine Description: + + Procedure processes the remote connection requtest: SABME. + It allocates a new link from the pool of closed links in + the SAP and runs the state machine. + +Arguments: + + pAdapterContext - The Adapter Binding specified at initialization time. + pSap - the current SAP handle + LlcHeader - LLC header + +Return Value: + + None. + +--*/ + +{ + PDATA_LINK pLink; + DLC_STATUS Status; + + ASSUME_IRQL(DISPATCH_LEVEL); + + RELEASE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); + + // + // The destination sap cannot be a group SAP any more, + // thus we don't need to mask the lowest bit aways + // + + Status = LlcOpenLinkStation( + pSap, + (UCHAR)(LlcHeader.auchRawBytes[DLC_SSAP_OFFSET] & 0xfe), + NULL, + pAdapterContext->pHeadBuf, + NULL, // no client handle => DLC driver must create it + (PVOID*)&pLink + ); + + ACQUIRE_SPIN_LOCK(&pAdapterContext->ObjectDataBase); + + // + // We can do nothing, if we are out of resources + // + + if (Status != STATUS_SUCCESS) { + return; + } + + ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock); + + // + // RLF 05/09/94 + // + // We need to keep a per-connection indication of the framing type if the + // adapter was opened in AUTO mode (else we generate 802.3 UA to DIX SABME) + // Only do this for Ethernet adapters (we only set the SNA DIX frame + // indicator in that case) + // + + pLink->FramingType = (IS_SNA_DIX_FRAME(pAdapterContext) + && IS_AUTO_BINDING(pLink->Gen.pLlcBinding)) + ? LLC_SEND_802_3_TO_DIX + : pLink->Gen.pLlcBinding->InternalAddressTranslation + ; + + // + // now create the Link Station by running the FSM with ACTIVATE_LS as input. + // This just initializes the link station 'object'. Then run the FSM again, + // this time with the SABME command as input + // + + RunStateMachineCommand(pLink, ACTIVATE_LS); + RunStateMachine( + pLink, + (USHORT)((LlcHeader.U.Command & LLC_U_POLL_FINAL) ? SABME1 : SABME0), + (BOOLEAN)((LlcHeader.U.Command & LLC_U_POLL_FINAL) ? 1 : 0), + (BOOLEAN)TRUE + ); + + RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock); +} + + +VOID +LlcTransferData( + IN PBINDING_CONTEXT pBindingContext, + IN PLLC_PACKET pPacket, + IN PMDL pMdl, + IN UINT uiCopyOffset, + IN UINT cbCopyLength + ) + +/*++ + +Routine Description: + + This function copies only the data part of the received frame - that is + the area after the LLC and DLC headers. If NDIS handed us all the data + in the lookahead buffer, then WE can copy it out. Otherwise we have to + call NDIS to get the data. + + If this is a DIX format frame, then NDIS thinks that the LAN header is + 14 bytes, but we know it is 17. We have to tell NDIS to copy from 3 bytes + further into the data part of the received frame than we would normally + have to + +Arguments: + + pBindingContext - binding handle + pPacket - receive context packet + pMdl - pointer to MDL describing data to copy + uiCopyOffset - offset from start of mapped buffer to copy from + cbCopyLength - length to copy + +Return Value: + + None. + +--*/ + +{ + PADAPTER_CONTEXT pAdapterContext = pBindingContext->pAdapterContext; + + pPacket->Data.Completion.CompletedCommand = LLC_RECEIVE_COMPLETION; + + // + // if the amount of data to copy is contained within the lookahead buffer + // then we can copy the data. + // + // Remember: pAdapterContext->cbLookBuf and pLookBuf have been correctly + // adjusted in the case of a DIX format frame + // + + if (pAdapterContext->cbLookBuf - uiCopyOffset >= cbCopyLength) { + + PUCHAR pSrcBuffer; + UINT BufferLength; + + pSrcBuffer = pAdapterContext->pLookBuf + uiCopyOffset; + + do { + if (cbCopyLength > MmGetMdlByteCount(pMdl)) { + BufferLength = MmGetMdlByteCount(pMdl); + } else { + BufferLength = cbCopyLength; + } + + // + // In 386 memcpy is faster than RtlMoveMemory, it also + // makes the full register optimization much easier, because + // all registers are available (no proc calls within loop) + // + + // + // !!!! Can't use LlcMemCpy here: On mips expands to RtlMoveMemory + // which uses FP registers. This won't work with shared memory + // on TR card + // + + safe_memcpy(MmGetSystemAddressForMdl(pMdl), pSrcBuffer, BufferLength); + pMdl = pMdl->Next; + pSrcBuffer += BufferLength; + cbCopyLength -= BufferLength; + } while (cbCopyLength); + pPacket->Data.Completion.Status = STATUS_SUCCESS; + pBindingContext->TransferDataPacket.pPacket = pPacket; + + } else { + + // + // too bad: there is more data to copy than is available in the look + // ahead buffer. We have to call NDIS to perform the copy + // + + UINT BytesCopied; + + // + // if this is an Ethernet adapter and the received LAN header length is + // more than 14 bytes then this is a DIX frame. We need to let NDIS know + // that we want to copy data from 3 bytes in from where it thinks the + // DLC header starts + // + + UINT additionalOffset = (pAdapterContext->NdisMedium == NdisMedium802_3) + ? (pAdapterContext->RcvLanHeaderLength > 14) + ? 3 + : 0 + : 0; + +#if DBG + if (additionalOffset) { + ASSERT(pAdapterContext->RcvLanHeaderLength == 17); + } +#endif + + // + // Theoretically NdisTransferData may not complete + // immediately, and we cannot add to the completion + // list, because the command is not really complete. + // We may save it to adapter context to wait the + // NdisTransferData to complete. + // + + if (pBindingContext->TransferDataPacket.pPacket != NULL) { + + // + // BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG + // + // If the same LLC client tries to receive the same buffer + // many times (eg. when receive a packet to a group sap) and + // if NDIS would complete those commands asynchronously, then + // we cannot receive the frame with NdisTransferData. + // Fortunately all NDIS implemnetations completes NdisTransferData + // synchronously. + // Solution: We could chain new packets to the existing data transfer + // packet, and copy the data, when the first data transfer request + // completes. This would mean a lot of code, that would never + // used by anyone. We would also need MDL to MDL copy function. + // The first data transfer could also be a smaller that another + // after it => would not work in a very general case, but + // would work with the group saps (all receives would be the same + // => direct MDL -> MDL copy would be OK. + // + // BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG-BUG + // + + pPacket->Data.Completion.Status = DLC_STATUS_ASYNC_DATA_TRANSFER_FAILED; + + pBindingContext->pfCommandComplete(pBindingContext->hClientContext, + pPacket->Data.Completion.hClientHandle, + pPacket + ); + } + + pBindingContext->TransferDataPacket.pPacket = pPacket; + pPacket->pBinding = pBindingContext; + ResetNdisPacket(&pBindingContext->TransferDataPacket); + NdisChainBufferAtFront((PNDIS_PACKET)&pBindingContext->TransferDataPacket, pMdl); + + // + // ADAMBA - Removed pAdapterContext->RcvLanHeaderLength + // from ByteOffset (the fourth param). + // + + NdisTransferData((PNDIS_STATUS)&pPacket->Data.Completion.Status, + pAdapterContext->NdisBindingHandle, + pAdapterContext->MacReceiveContext, + + // + // if this is a DIX frame we have to move the data + // pointer ahead by the amount in additionalOffset + // (should always be 3 in this case) and reduce the + // amount of data to copy by the same number + // + + uiCopyOffset + additionalOffset, + + // + // we DON'T need to account for the additionalOffset + // in the length to be copied though + // + + cbCopyLength, + (PNDIS_PACKET)&pBindingContext->TransferDataPacket, + &BytesCopied + ); + } + + // + // We must queue a packet for the final receive completion, + // But we cannot do it until TransferData is completed + // (it is actually always completed, but this code is just + // for sure). + // + + if (pPacket->Data.Completion.Status != NDIS_STATUS_PENDING + && pAdapterContext->LinkRcvStatus != STATUS_PENDING) { + + // + // We receive the data before it is checked by the link station. + // The upper protocol must just setup asynchronous receive + // and later in LLC_RECEIVE_COMPLETION handling to + // discard the receive, if it failed or accept if + // it was OK for NDIS and link station. + // + + if (pAdapterContext->LinkRcvStatus != STATUS_SUCCESS) { + pPacket->Data.Completion.Status = pAdapterContext->LinkRcvStatus; + } + + ACQUIRE_DRIVER_LOCK(); + + pBindingContext->pfCommandComplete(pBindingContext->hClientContext, + pPacket->Data.Completion.hClientHandle, + pPacket + ); + + RELEASE_DRIVER_LOCK(); + + pBindingContext->TransferDataPacket.pPacket = NULL; + } +} + + +VOID +LlcNdisTransferDataComplete( + IN PADAPTER_CONTEXT pAdapterContext, + IN PNDIS_PACKET pPacket, + IN NDIS_STATUS NdisStatus, + IN UINT uiBytesTransferred + ) + +/*++ + +Routine Description: + + The routine handles NdisCompleteDataTransfer indication and + queues the indication of the completed receive operation. + +Arguments: + + pAdapterContext - adapter context + pPacket - NDIS packet used in the data transfer + NdisStatus - status of the completed data transfer + uiBytesTransferred - who needs this, I am not interested in + the partially succeeded data transfers, + +Return Value: + + None. + +--*/ + +{ + KIRQL OldIrql; + + UNREFERENCED_PARAMETER(uiBytesTransferred); + UNREFERENCED_PARAMETER(OldIrql); + + ASSUME_IRQL(DISPATCH_LEVEL); + + ACQUIRE_DRIVER_LOCK(); + + if (((PLLC_TRANSFER_PACKET)pPacket)->pPacket != NULL) { + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket->Data.Completion.Status = NdisStatus; + + // + // I- frames have two statuses. The link state machine is executed + // after the NdisDataTransfer and thus its returned status may still + // cancel the command. There are no spin locks around the return status + // handling, but this should still work fine. It actually does not + // matter if we return NDIS or state machine error code + // + + if (pAdapterContext->LinkRcvStatus != STATUS_PENDING) { + if (pAdapterContext->LinkRcvStatus != STATUS_SUCCESS) { + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket->Data.Completion.Status = pAdapterContext->LinkRcvStatus; + } + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket->pBinding->pfCommandComplete( + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket->pBinding->hClientContext, + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket->Data.Completion.hClientHandle, + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket + ); + ((PLLC_TRANSFER_PACKET)pPacket)->pPacket = NULL; + } + } + + RELEASE_DRIVER_LOCK(); +} + + +VOID +safe_memcpy( + OUT PUCHAR Destination, + IN PUCHAR Source, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This is here because on a MIPS machine, LlcMemCpy expands to RtlMoveMemory + which wants to use 64-bit floating point (CP1) registers for memory moves + where both source and destination are aligned on 8-byte boundaries and + where the length is a multiple of 32 bytes. If the source or destination + buffer is actually the shared memory of a TR card, then the 64-bit moves + (saw it on read, presume same for write) can only access memory in 32-bit + chunks and 01 02 03 04 05 06 07 08 gets converted to 01 02 03 04 01 02 03 04. + So this function attempts to do basically the same, without all the smarts + as the original, but doesn't employ coprocessor registers to achieve the + move. Hence slower, but safer + +Arguments: + + Destination - where we're copying to + Source - where we're copying from + Length - how many bytes to move + +Return Value: + + None. + +--*/ + +{ + ULONG difference = (ULONG)Destination - (ULONG)Source; + INT i; + + if (!(difference && Length)) { + return; + } + + // + // if the destination overlaps the source then do reverse copy. Add a little + // optimization - a la RtlMoveMemory - try to copy as many bytes as DWORDS. + // However, on MIPS, both source and destination must be DWORD aligned to + // do this. If both aren't then fall-back to BYTE copy + // + + if (difference < Length) { + if (!(((ULONG)Destination & 3) || ((ULONG)Source & 3))) { + Destination += Length; + Source += Length; + + for (i = Length % 4; i; --i) { + *--Destination = *--Source; + } + for (i = Length / 4; i; --i) { + *--((PULONG)Destination) = *--((PULONG)Source); + } + } else { + Destination += Length; + Source += Length; + + while (Length--) { + *--Destination = *--Source; + } + } + } else { + if (!(((ULONG)Destination & 3) || ((ULONG)Source & 3))) { + for (i = Length / 4; i; --i) { + *((PULONG)Destination)++ = *((PULONG)Source)++; + } + for (i = Length % 4; i; --i) { + *Destination++ = *Source++; + } + } else { + while (Length--) { + *Destination++ = *Source++; + } + } + } +} + + +BOOLEAN +FramingDiscoveryCacheHit( + IN PADAPTER_CONTEXT pAdapterContext, + IN PBINDING_CONTEXT pBindingContext + ) + +/*++ + +Routine Description: + + This function is called when we receive a TEST/XID/SABME frame AND the + adapter binding was created with LLC_ETHERNET_TYPE_AUTO AND we opened an + ethernet adapter. + + The frame has either 802.3 or DIX framing. For all command and response TEST + and XID frames and all SABME frames received, we keep note of the MAC address + where the frame originated and its framing type. + + The first time we receive one of the above frames from a particular MAC + address, the info will not be in the cache. So we add it. Subsequent frames + of the above type (all others are passed through) with the same framing type + as that in the cache will be indicated to the higher layers. If one of the + above frame types arrives with THE OPPOSITE framing type (i.e. DIX instead + of 802.3) then when we look in the cache for the MAC address we will find + that it is already there, but with a different framing type (i.e. 802.3 + instead of DIX). In this case, we assume that the frame is an automatic + duplicate and we discard it + + NOTE: We don't have to worry about UA because we only expect one SABME to + be accepted: either we're sending the duplicate SABME and the target machine + is configured for 802.3 or DIX, BUT NOT BOTH, or the receiving machine is + another NT box running this DLC (with caching enabled!) and it will filter + out the duplicate. Hence, in both situations, only one UA response should be + generated per the SABME 'event' + + ASSUMES: The tick count returned from the system never wraps (! 2^63/10^7 + == 29,247+ years) + +Arguments: + + pAdapterContext - pointer to ADAPTER_CONTEXT which has been filled in with + pHeadBuf pointing to - at least - the first 14 bytes in + the frame header + pBindingContext - pointer to BINDING_CONTEXT containing the EthernetType + and if LLC_ETHERNET_TYPE_AUTO, the address of the framing + discovery cache + +Return Value: + + BOOLEAN + TRUE - the MAC address was found in the cache WITH THE OTHER FRAMING + TYPE. Therefore the current frame should be discarded + FALSE - the MAC address/framing type combination was not found. The + frame should be indicated to the higher layer. If caching is + enabled, the frame has been added to the cache + +--*/ + +{ + ULONG i; + ULONG lruIndex; + LARGE_INTEGER timeStamp; + NODE_ADDRESS nodeAddress; + PFRAMING_DISCOVERY_CACHE_ENTRY pCache; + UCHAR framingType; + + // + // if the binding context was not created with LLC_ETHERNET_TYPE_AUTO (and + // therefore by implication, adapter is not ethernet) OR framing discovery + // caching is disabled (the value read from the registry was zero) then bail + // out with a not-found indication + // + + if ((pBindingContext->EthernetType != LLC_ETHERNET_TYPE_AUTO) + || (pBindingContext->FramingDiscoveryCacheEntries == 0)) { + +#if defined(DEBUG_DISCOVERY) + + DbgPrint("FramingDiscoveryCacheHit: Not AUTO or 0 cache: returning FALSE\n"); + +#endif + + return FALSE; + } + +#if defined(DEBUG_DISCOVERY) + + { + // + // BUGBUG - even though this is debug code, we shouldn't really be + // indexing so far into pHeadBuf. Its only guaranteed to be + // 14 bytes long. Should be looking in pLookBuf[5] and [2] + // + + UCHAR frame = (pAdapterContext->pHeadBuf[12] == 0x80) + ? pAdapterContext->pHeadBuf[19] + : pAdapterContext->pHeadBuf[16]; + + frame &= ~0x10; // knock off Poll/Final bit + + DbgPrint("FramingDiscoveryCacheHit: Received: %02x-%02x-%02x-%02x-%02x-%02x %s %s (%02x)\n", + pAdapterContext->pHeadBuf[6], + pAdapterContext->pHeadBuf[7], + pAdapterContext->pHeadBuf[8], + pAdapterContext->pHeadBuf[9], + pAdapterContext->pHeadBuf[10], + pAdapterContext->pHeadBuf[11], + (pAdapterContext->pHeadBuf[12] == 0x80) + ? "DIX" + : "802.3", + (frame == 0xE3) + ? "TEST" + : (frame == 0xAF) + ? "XID" + : (frame == 0x6F) + ? "SABME" + : (frame == 0x63) + ? "UA" + : "???", + frame + ); + } + +#endif + + // + // set up and perform a linear search of the cache (it should be reasonably + // small and the comparisons are ULONG & USHORT, so not time critical + // + + lruIndex = 0; + + // + // better make sure we don't get data misalignment on MIPS + // + + nodeAddress.Words.Top4 = *(ULONG UNALIGNED *)&pAdapterContext->pHeadBuf[6]; + nodeAddress.Words.Bottom2 = *(USHORT UNALIGNED *)&pAdapterContext->pHeadBuf[10]; + pCache = pBindingContext->FramingDiscoveryCache; + + // + // framingType is the type we are looking for in the cache, not the type + // in the frame + // + + framingType = ((pAdapterContext->pHeadBuf[12] == 0x80) + && (pAdapterContext->pHeadBuf[13] == 0xD5)) + ? FRAMING_TYPE_802_3 + : FRAMING_TYPE_DIX + ; + + // + // get the current tick count for comparison of time stamps + // + + KeQueryTickCount(&timeStamp); + + // + // linear search the cache + // + + for (i = 0; i < pBindingContext->FramingDiscoveryCacheEntries; ++i) { + if (pCache[i].InUse) { + if ((pCache[i].NodeAddress.Words.Top4 == nodeAddress.Words.Top4) + && (pCache[i].NodeAddress.Words.Bottom2 == nodeAddress.Words.Bottom2)) { + + // + // we found the destination MAC address. If it has the opposite + // framing type to that in the frame just received, return TRUE + // else FALSE. In both cases refresh the time stamp + // + + pCache[i].TimeStamp = timeStamp; + +#if defined(DEBUG_DISCOVERY) + + DbgPrint("FramingDiscoveryCacheHit: Returning %s. Index = %d\n\n", + (pCache[i].FramingType == framingType) ? "TRUE" : "FALSE", + i + ); + +#endif + + return (pCache[i].FramingType == framingType); + } else if (pCache[i].TimeStamp.QuadPart < timeStamp.QuadPart) { + + // + // if we need to throw out a cache entry, we throw out the one + // with the oldest time stamp + // + + timeStamp = pCache[i].TimeStamp; + lruIndex = i; + } + } else { + + // + // we have hit an unused entry. The destination address/framing type + // cannot be in the cache: add the received address/framing type at + // this unused location + // + + lruIndex = i; + break; + } + } + + // + // the destination address/framing type combination are not in the cache. + // Add them. Throw out an entry if necessary + // + +#if defined(DEBUG_DISCOVERY) + + DbgPrint("FramingDiscoveryCacheHit: Adding/Throwing out %d (time stamp %08x.%08x\n", + lruIndex, + pCache[lruIndex].TimeStamp.HighPart, + pCache[lruIndex].TimeStamp.LowPart + ); + +#endif + + pCache[lruIndex].NodeAddress.Words.Top4 = nodeAddress.Words.Top4; + pCache[lruIndex].NodeAddress.Words.Bottom2 = nodeAddress.Words.Bottom2; + pCache[lruIndex].InUse = TRUE; + pCache[lruIndex].FramingType = (framingType == FRAMING_TYPE_DIX) + ? FRAMING_TYPE_802_3 + : FRAMING_TYPE_DIX + ; + pCache[lruIndex].TimeStamp = timeStamp; + + // + // return FALSE meaning the destination address/framing type just received + // was not in the cache (but it is now) + // + +#if defined(DEBUG_DISCOVERY) + + DbgPrint("FramingDiscoveryCacheHit: Returning FALSE\n\n"); + +#endif + + return FALSE; +} |