1590 lines
49 KiB
C
1590 lines
49 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
Copyright (c) 1991 Nokia Data Systems AB
|
||
|
||
Module Name:
|
||
|
||
dlcindc.c
|
||
|
||
Abstract:
|
||
|
||
This module includes primitives to handle all events
|
||
and indications from the LLC (802.2 data link) module.
|
||
|
||
Contents:
|
||
LlcReceiveIndication
|
||
LlcEventIndication
|
||
LlcCommandCompletion
|
||
CompleteTransmitCommand
|
||
CompleteDlcCommand
|
||
|
||
Author:
|
||
|
||
Antti Saarenheimo 01-Sep-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
//
|
||
// This define enables the private DLC function prototypes
|
||
// We don't want to export our data types to the llc layer.
|
||
// MIPS compiler doesn't accept hiding of the internal data
|
||
// structures by a PVOID in the function prototype.
|
||
// i386 will check the type defines
|
||
//
|
||
|
||
#ifndef i386
|
||
#define DLC_PRIVATE_PROTOTYPES
|
||
#endif
|
||
#include <dlc.h>
|
||
#include <smbgtpt.h>
|
||
|
||
#if 0
|
||
|
||
//
|
||
// if DLC and LLC share the same driver then we can use macros to access fields
|
||
// in the BINDING_CONTEXT and ADAPTER_CONTEXT structures
|
||
//
|
||
|
||
#if DLC_AND_LLC
|
||
#ifndef i386
|
||
#define LLC_PRIVATE_PROTOTYPES
|
||
#endif
|
||
#include "llcdef.h"
|
||
#include "llctyp.h"
|
||
#include "llcapi.h"
|
||
#endif
|
||
#endif
|
||
|
||
//
|
||
// Table includes all llc header length of different frame types
|
||
//
|
||
|
||
static UCHAR aDlcHeaderLengths[LLC_LAST_FRAME_TYPE / 2] = {
|
||
0, // DLC_UNDEFINED_FRAME_TYPE = 0,
|
||
0, // DLC_MAC_FRAME = 0x02,
|
||
4, // DLC_I_FRAME = 0x04,
|
||
3, // DLC_UI_FRAME = 0x06,
|
||
3, // DLC_XID_COMMAND_POLL = 0x08,
|
||
3, // DLC_XID_COMMAND_NOT_POLL = 0x0a,
|
||
3, // DLC_XID_RESPONSE_FINAL = 0x0c,
|
||
3, // DLC_XID_RESPONSE_NOT_FINAL = 0x0e,
|
||
3, // DLC_TEST_RESPONSE_FINAL = 0x10,
|
||
3, // DLC_TEST_RESPONSE_NOT_FINAL = 0x12,
|
||
0, // DLC_DIRECT_8022 = 0x14,
|
||
3, // DLC_TEST_COMMAND = 0x16,
|
||
0 // DLC_DIRECT_ETHERNET_TYPE = 0x18
|
||
};
|
||
|
||
|
||
DLC_STATUS
|
||
LlcReceiveIndication(
|
||
IN PDLC_FILE_CONTEXT pFileContext,
|
||
IN PDLC_OBJECT pDlcObject,
|
||
IN USHORT FrameType,
|
||
IN PUCHAR pLookBuf,
|
||
IN UINT cbPacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primitive handles the receive data indication from
|
||
the lower level
|
||
the returned parameter block of the read command.
|
||
|
||
IBM has successfully made the receive extremly complicated.
|
||
We can distinguish at least four different ways to gather the
|
||
receive information to the frame header, when we have received
|
||
an I- frame for a link station:
|
||
|
||
1. Rcv command for link station, frames linked in link basis
|
||
- user length and read options from link object
|
||
- receive buffer base in link object
|
||
- station information from link object
|
||
|
||
2. Rcv command for link station, frames linked in sap bases
|
||
- user length and read options from link object
|
||
- receive buffer base in sap object
|
||
- station information from link object
|
||
|
||
3. Rcv command only for sap station, frames linked in link basis
|
||
- user length and read options from sap object
|
||
- receive buffer base in link object
|
||
- station information from link object
|
||
|
||
4. Rcv command only for sap station, frames linked in sap basis
|
||
- user length and read options from sap object
|
||
- receive buffer base in sap object
|
||
- station information from link object
|
||
|
||
=> We have three different DLC objects in receive:
|
||
|
||
1. The orginal destination of the frame, we will read station id
|
||
from that object.
|
||
2. The owner of the receive, the read command must match to the
|
||
station id of the events owner. The owner also chains the
|
||
the received data in its frame list.
|
||
3. Receive object: the receive object defines the recieve options
|
||
saved to the frame header and the read flag saved to the read
|
||
parameters.
|
||
|
||
At least two objects are same (the different ones in different cases),
|
||
and in most cases all objects are the same dlc object.
|
||
|
||
We will need to save these in rcv event:
|
||
- The owner dlc object
|
||
- Pointer to linked frame header list or to a single frame
|
||
(defined by the receive read option in the next object).
|
||
We can directly use the owner object, because the frames
|
||
need to be chained. In that case we must save directly the
|
||
reference of the buffer header.
|
||
- The receive object, the dlc object having a pending receive,
|
||
that was used to received this event.
|
||
|
||
Arguments:
|
||
|
||
pFileContext - the device context of this DLC client
|
||
pDlcObject - the DLC client, that received the event.
|
||
FrameType - current frame type
|
||
pLookBuf - points to the data from the LLC header (ie. excludes the
|
||
LAN header). MAY NOT CONTAIN ALL DATA
|
||
cbPacketSize - amount of data to copy, including DLC header, but not
|
||
including LLC header
|
||
|
||
Return Value:
|
||
|
||
DLC_STATUS:
|
||
|
||
--*/
|
||
|
||
{
|
||
PDLC_OBJECT pRcvObject = pDlcObject;
|
||
PDLC_OBJECT pOwnerObject;
|
||
PDLC_BUFFER_HEADER pBufferHeader;
|
||
DLC_STATUS Status = STATUS_SUCCESS;
|
||
NTSTATUS NtStatus;
|
||
UINT uiLlcOffset;
|
||
UINT FrameHeaderSize;
|
||
UINT LlcLength;
|
||
PDLC_EVENT pRcvEvent;
|
||
UINT DataSize;
|
||
PFIRST_DLC_SEGMENT pFirstBuffer;
|
||
PDLC_COMMAND pDlcCommand;
|
||
UINT BufferSizeLeft;
|
||
|
||
//
|
||
// this function is called in the context of a DPC: it is the receive data
|
||
// indication from NDIS
|
||
//
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE('D');
|
||
|
||
if (pFileContext->State != DLC_FILE_CONTEXT_OPEN) {
|
||
return DLC_STATUS_ADAPTER_CLOSED;
|
||
}
|
||
|
||
ENTER_DLC(pFileContext);
|
||
|
||
#if LLC_DBG
|
||
|
||
if (pDlcObject->State > DLC_OBJECT_CLOSED) {
|
||
DbgPrint("Invalid object type!");
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// Search the first object having a pending receive, loop
|
||
// the link, the sap and the direct station until we find one.
|
||
//
|
||
|
||
while (pRcvObject != NULL && pRcvObject->pRcvParms == NULL) {
|
||
if (pRcvObject->Type == DLC_LINK_OBJECT) {
|
||
pRcvObject = pRcvObject->u.Link.pSap;
|
||
} else if (pRcvObject->Type == DLC_SAP_OBJECT) {
|
||
pRcvObject = pFileContext->SapStationTable[0];
|
||
} else if (pRcvObject->Type == DLC_DIRECT_OBJECT) {
|
||
pRcvObject = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// return error status if we cannot find any receive command.
|
||
//
|
||
|
||
if (pRcvObject == NULL) {
|
||
Status = DLC_STATUS_NO_RECEIVE_COMMAND;
|
||
|
||
//#if DBG
|
||
// DbgPrint("DLC.LlcReceiveIndication.%d: Error: No receive command\n", __LINE__);
|
||
//#endif
|
||
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// Now we must figure out the actual owner of the received frame.
|
||
// There are actually only two special cases:
|
||
//
|
||
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.uchRcvReadOption == LLC_RCV_CHAIN_FRAMES_ON_LINK
|
||
&& pRcvObject->Type == DLC_SAP_OBJECT) {
|
||
pOwnerObject = pDlcObject;
|
||
} else if (pRcvObject->pRcvParms->Async.Parms.Receive.uchRcvReadOption == LLC_RCV_CHAIN_FRAMES_ON_SAP
|
||
&& pRcvObject->Type == DLC_LINK_OBJECT) {
|
||
pOwnerObject = pRcvObject->u.Link.pSap;
|
||
} else {
|
||
|
||
//
|
||
// In all other cases we chain the frames to the receive object
|
||
// (actually IBM has not defined the case), the frames are
|
||
// chained for direct if the rcv read option is set chaining for
|
||
// sap or link station
|
||
//
|
||
|
||
pOwnerObject = pRcvObject;
|
||
|
||
//
|
||
// direct station can recieve only the frame
|
||
// types defined by station id of the receive command
|
||
// There are three types, we need to check the one, that does not work:
|
||
//
|
||
// DLC_DIRECT_ALL_FRAMES 0
|
||
// DLC_DIRECT_MAC_FRAMES 1
|
||
// DLC_DIRECT_NON_MAC_FRAMES 2
|
||
//
|
||
|
||
if (pRcvObject->Type == DLC_DIRECT_OBJECT) {
|
||
if (FrameType == LLC_DIRECT_MAC) {
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.usStationId == LLC_DIRECT_8022) {
|
||
Status = DLC_STATUS_NO_RECEIVE_COMMAND;
|
||
goto ErrorExit;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// It must be a non-MAC frame
|
||
//
|
||
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.usStationId == LLC_DIRECT_MAC) {
|
||
Status = DLC_STATUS_NO_RECEIVE_COMMAND;
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The frame length must be known when the buffers are allocated,
|
||
// This may not be the same as the actual length of the received
|
||
// LAN header (if we received a DIX frame)
|
||
//
|
||
|
||
uiLlcOffset = LlcGetReceivedLanHeaderLength(pFileContext->pBindingContext);
|
||
|
||
//
|
||
// Check first the buffer type (contiguous or non contiguous),
|
||
// and then allocate it.
|
||
// Note: WE DO NOT SUPPORT THE BREAK OPTION (because it would make
|
||
// the current buffer management even more complicated)
|
||
//
|
||
|
||
LlcLength = aDlcHeaderLengths[FrameType / 2];
|
||
|
||
//
|
||
// DIX frames are a special case: they must be filtered
|
||
// (DIX Llc header == ethernet type word is always 2 bytes,
|
||
// nobody else use this llc type size).
|
||
// A DIX application may define an offset, mask and match to
|
||
// filter only those frames it is really needing.
|
||
// This method works very well with XNS and TCP socket types
|
||
//
|
||
|
||
if ( LlcLength > cbPacketSize ) {
|
||
Status = DLC_STATUS_INVALID_FRAME_LENGTH;
|
||
goto ErrorExit;
|
||
}
|
||
|
||
if ((FrameType == LLC_DIRECT_ETHERNET_TYPE)
|
||
&& (pDlcObject->u.Direct.ProtocolTypeMask != 0)) {
|
||
|
||
ULONG ProtocolType;
|
||
|
||
//
|
||
// there's a real good possibility here that if the app supplies a very
|
||
// large value for the protocol offset, we will blow up - there's no
|
||
// range checking performed!
|
||
//
|
||
|
||
ASSERT(pDlcObject->u.Direct.ProtocolTypeOffset >= 14);
|
||
|
||
//
|
||
// let's add that range check: if the protocol offset is before the
|
||
// data part of the frame or past the end of this particular frame
|
||
// then we say there's no receive defined for this frame
|
||
//
|
||
|
||
if ((pDlcObject->u.Direct.ProtocolTypeOffset < 14)
|
||
|| (pDlcObject->u.Direct.ProtocolTypeOffset > cbPacketSize + 10)) {
|
||
return DLC_STATUS_NO_RECEIVE_COMMAND;
|
||
}
|
||
|
||
//
|
||
// the offset to the protocol type field is given as offset from the
|
||
// start of the frame: we only get to look in the lookahead buffer,
|
||
// but we know that since this is an ethernet frame, the lookahead
|
||
// buffer starts 14 bytes into the frame, so remove this length from
|
||
// the protocol offset
|
||
//
|
||
|
||
ProtocolType = SmbGetUlong(&pLookBuf[pDlcObject->u.Direct.ProtocolTypeOffset - 14]);
|
||
|
||
if ((ProtocolType & pDlcObject->u.Direct.ProtocolTypeMask) != pDlcObject->u.Direct.ProtocolTypeMatch) {
|
||
return DLC_STATUS_NO_RECEIVE_COMMAND;
|
||
}
|
||
}
|
||
|
||
//
|
||
// The created MDL must not include the LAN header because it is not copied
|
||
// by LlcTransferData. We use a temporary frame header size to allocate space
|
||
// for the LAN header. The LLC header will be copied just as any other data
|
||
//
|
||
|
||
if (FrameType == LLC_DIRECT_MAC) {
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.uchOptions & DLC_CONTIGUOUS_MAC) {
|
||
FrameHeaderSize = sizeof(DLC_CONTIGUOUS_RECEIVE) + uiLlcOffset;
|
||
DataSize = cbPacketSize;
|
||
} else {
|
||
FrameHeaderSize = sizeof(DLC_NOT_CONTIGUOUS_RECEIVE);
|
||
DataSize = cbPacketSize - LlcLength;
|
||
}
|
||
} else {
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.uchOptions & DLC_CONTIGUOUS_DATA) {
|
||
FrameHeaderSize = sizeof(DLC_CONTIGUOUS_RECEIVE) + uiLlcOffset;
|
||
DataSize = cbPacketSize;
|
||
} else {
|
||
FrameHeaderSize = sizeof(DLC_NOT_CONTIGUOUS_RECEIVE);
|
||
DataSize = cbPacketSize - LlcLength;
|
||
}
|
||
}
|
||
|
||
pBufferHeader = NULL;
|
||
NtStatus = BufferPoolAllocate(
|
||
#if DBG
|
||
pFileContext,
|
||
#endif
|
||
pFileContext->hBufferPool,
|
||
DataSize, // size of actual MDL buffers
|
||
FrameHeaderSize, // frame hdr (and possibly lan hdr)
|
||
pRcvObject->pRcvParms->Async.Parms.Receive.usUserLength,
|
||
cbPacketSize + uiLlcOffset, // size of the packet
|
||
(UINT)(-1), // any size is OK.
|
||
&pBufferHeader, // returned buffer pointer
|
||
&BufferSizeLeft
|
||
);
|
||
if (NtStatus != STATUS_SUCCESS) {
|
||
if (FrameType != LLC_I_FRAME) {
|
||
|
||
//
|
||
// We must complete the receive with the given error status,
|
||
// if this frame is not a I- frame. (I-frames can be dropped
|
||
// to the floor, the other frames completes the receive with
|
||
// an error status)
|
||
// -----------------------------------------------
|
||
// We should not complete commands in receive lookahead.
|
||
// The correct way could be to queue this somehow in
|
||
// data link and process this, when the command is completed
|
||
// in the command completion indication.
|
||
// On the other hand, NBF DOES THIS FOR EVERY IRP!
|
||
//
|
||
|
||
pDlcCommand = SearchAndRemoveCommandByHandle(
|
||
&pFileContext->ReceiveQueue,
|
||
(ULONG)-1,
|
||
(USHORT)DLC_IGNORE_STATION_ID,
|
||
(USHORT)DLC_STATION_MASK_SPECIFIC,
|
||
pRcvObject->pRcvParms->Async.Ccb.pCcbAddress
|
||
);
|
||
|
||
//
|
||
// RLF 11/24/92
|
||
//
|
||
// if pDlcCommand is NULL then check the command queue - this may
|
||
// be a receive without a RECEIVE_FLAG parameter
|
||
//
|
||
|
||
if (!pDlcCommand) {
|
||
pDlcCommand = SearchAndRemoveCommandByHandle(
|
||
&pFileContext->CommandQueue,
|
||
(ULONG)-1,
|
||
(USHORT)DLC_IGNORE_STATION_ID,
|
||
(USHORT)DLC_STATION_MASK_SPECIFIC,
|
||
pRcvObject->pRcvParms->Async.Ccb.pCcbAddress
|
||
);
|
||
ASSERT(pDlcCommand);
|
||
}
|
||
|
||
pRcvObject->pRcvParms = NULL;
|
||
|
||
#if LLC_DBG
|
||
|
||
DbgPrint("cFramesReceived: %x\n", cFramesReceived);
|
||
DbgPrint("cFramesIndicated: %x\n", cFramesIndicated);
|
||
DbgPrint("cFramesReleased: %x\n", cFramesReleased);
|
||
|
||
if (pDlcCommand == NULL) {
|
||
DbgPrint("Lost receive command???");
|
||
} else
|
||
|
||
#endif
|
||
|
||
CompleteDlcCommand(pFileContext,
|
||
pRcvObject->StationId,
|
||
pDlcCommand,
|
||
DLC_STATUS_LOST_DATA_NO_BUFFERS
|
||
);
|
||
}
|
||
|
||
//
|
||
// Free the partial buffer
|
||
//
|
||
|
||
BufferPoolDeallocateList(pFileContext->hBufferPool, pBufferHeader);
|
||
|
||
//#if DBG
|
||
// DbgPrint("DLC.LlcReceiveIndication.%d: Error: Out of receive buffers\n", __LINE__);
|
||
//#endif
|
||
|
||
Status = DLC_STATUS_OUT_OF_RCV_BUFFERS;
|
||
goto ErrorExit;
|
||
}
|
||
|
||
//
|
||
// A link station may have committed memory from the buffer pool
|
||
// when it local busy state was enabled after a local busy state
|
||
// because of 'out of receive buffers'. We must uncommit all
|
||
// packets received by that link station until the size of
|
||
// the commited buffer space is zero
|
||
//
|
||
|
||
if (pDlcObject->CommittedBufferSpace != 0) {
|
||
|
||
ULONG UncommittedBufferSpace;
|
||
|
||
//
|
||
// get the smaller
|
||
//
|
||
|
||
UncommittedBufferSpace = (pDlcObject->CommittedBufferSpace < BufGetPacketSize(cbPacketSize)
|
||
? pDlcObject->CommittedBufferSpace
|
||
: BufGetPacketSize(cbPacketSize));
|
||
|
||
pDlcObject->CommittedBufferSpace -= UncommittedBufferSpace;
|
||
BufUncommitBuffers(pFileContext->hBufferPool, UncommittedBufferSpace);
|
||
}
|
||
|
||
//
|
||
// By default this is linked only to itself.
|
||
// We must create a event information every time,
|
||
// because app might read the old chained frames
|
||
// just between TransferData and its confirmation.
|
||
// => we cannot chain frames before TransmitData is confirmed.
|
||
// We should not either save any pointers to other objects,
|
||
// because they might disappear before the confirm
|
||
// (we use the pending transmit count to prevent OwnerObject
|
||
// to disappear before the confirm)
|
||
//
|
||
|
||
pBufferHeader->FrameBuffer.pNextFrame = pBufferHeader;
|
||
|
||
pRcvEvent = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
||
|
||
if (pRcvEvent == NULL) {
|
||
Status = DLC_STATUS_NO_MEMORY;
|
||
BufferPoolDeallocateList(pFileContext->hBufferPool, pBufferHeader);
|
||
|
||
#if DBG
|
||
DbgPrint("DLC.LlcReceiveIndication.%d: Error: Out of memory\n", __LINE__);
|
||
#endif
|
||
|
||
goto ErrorExit;
|
||
}
|
||
|
||
pRcvEvent->Event = LLC_RECEIVE_DATA;
|
||
pRcvEvent->StationId = pOwnerObject->StationId;
|
||
pRcvEvent->pOwnerObject = pOwnerObject;
|
||
pRcvEvent->Overlay.RcvReadOption = pRcvObject->pRcvParms->Async.Parms.Receive.uchRcvReadOption;
|
||
pRcvEvent->SecondaryInfo = pRcvObject->pRcvParms->Async.Parms.Receive.ulReceiveFlag;
|
||
pRcvEvent->pEventInformation = pBufferHeader;
|
||
pOwnerObject->PendingLlcRequests++;
|
||
|
||
pFirstBuffer = (PFIRST_DLC_SEGMENT)
|
||
((PUCHAR)pBufferHeader->FrameBuffer.pParent->Header.pGlobalVa
|
||
+ MIN_DLC_BUFFER_SEGMENT * pBufferHeader->FrameBuffer.Index);
|
||
|
||
pFirstBuffer->Cont.Options = pRcvObject->pRcvParms->Async.Parms.Receive.uchOptions;
|
||
pFirstBuffer->Cont.MessageType = (UCHAR)FrameType;
|
||
pFirstBuffer->Cont.BuffersLeft = (USHORT)(BufferPoolCount(pFileContext->hBufferPool));
|
||
pFirstBuffer->Cont.RcvFs = 0xCC;
|
||
pFirstBuffer->Cont.AdapterNumber = pFileContext->AdapterNumber;
|
||
pFirstBuffer->Cont.pNextFrame = NULL;
|
||
pFirstBuffer->Cont.StationId = pDlcObject->StationId;
|
||
|
||
//
|
||
// A receive command without read flag is used only once.
|
||
// The receive completion will complete also the receive command
|
||
//
|
||
|
||
if (pRcvObject->pRcvParms->Async.Parms.Receive.ulReceiveFlag == 0) {
|
||
pRcvObject->pRcvParms = NULL;
|
||
}
|
||
|
||
//
|
||
// copy NOT_CONTIGUOUS or CONTIGUOUS frame header to beginning of buffer
|
||
//
|
||
|
||
if (FrameHeaderSize == sizeof(DLC_NOT_CONTIGUOUS_RECEIVE)) {
|
||
pFirstBuffer->Cont.UserOffset = sizeof(DLC_NOT_CONTIGUOUS_RECEIVE);
|
||
LlcCopyReceivedLanHeader(pFileContext->pBindingContext,
|
||
pFirstBuffer->NotCont.LanHeader,
|
||
NULL
|
||
);
|
||
pFirstBuffer->NotCont.LanHeaderLength = (UCHAR)uiLlcOffset;
|
||
if (FrameType != LLC_DIRECT_ETHERNET_TYPE) {
|
||
pFirstBuffer->NotCont.DlcHeaderLength = (UCHAR)LlcLength;
|
||
LlcMemCpy((PCHAR)pFirstBuffer->NotCont.DlcHeader,
|
||
(PCHAR)pLookBuf,
|
||
LlcLength
|
||
);
|
||
} else {
|
||
|
||
USHORT ethernetType = LlcGetEthernetType(pFileContext->pBindingContext);
|
||
UCHAR byte = ethernetType & 0xff;
|
||
|
||
pFirstBuffer->NotCont.DlcHeaderLength = 2;
|
||
ethernetType >>= 8;
|
||
ethernetType |= ((USHORT)byte) << 8;
|
||
*(PUSHORT)&pFirstBuffer->NotCont.DlcHeader = ethernetType;
|
||
LlcLength = 0;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// We have included the LAN header size in the frame header size to
|
||
// make room for this copy, but now we fix the UserOffset and everything
|
||
// should be OK
|
||
//
|
||
|
||
LlcLength = 0;
|
||
pFirstBuffer->Cont.UserOffset = sizeof(DLC_CONTIGUOUS_RECEIVE);
|
||
LlcCopyReceivedLanHeader(pFileContext->pBindingContext,
|
||
(PCHAR)pFirstBuffer
|
||
+ sizeof(DLC_CONTIGUOUS_RECEIVE)
|
||
+ pFirstBuffer->Cont.UserLength,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
#if LLC_DBG
|
||
cFramesReceived++;
|
||
#endif
|
||
|
||
//
|
||
// Save the event only if this is the first chained frame.
|
||
// The sequential received frames will be queued behind it.
|
||
//
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
|
||
RELEASE_DRIVER_LOCK();
|
||
|
||
LlcTransferData(
|
||
pFileContext->pBindingContext, // data link adapter context
|
||
&(pRcvEvent->LlcPacket), // receive packet
|
||
pBufferHeader->FrameBuffer.pMdl, // destination mdl
|
||
LlcLength, // offset in LookBuf to copy from
|
||
cbPacketSize - LlcLength // length of copied data
|
||
);
|
||
|
||
ACQUIRE_DRIVER_LOCK();
|
||
|
||
//
|
||
// Transfer data returns always a pending status,
|
||
// the success/error status is returned asynchronously
|
||
// in the the receive indication completion (really?)
|
||
// We should copy the whole frame here, if is visiable
|
||
// in the receive lookahead buffer.
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
ErrorExit:
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
|
||
//
|
||
// The receive status is very important for I- frames,
|
||
// because the llc driver set the link busy when we return
|
||
// DLC_STATUS_NO_RECEIVE_COMMAND or DLC_STATUS_OUT_OF_RCV_BUFFERS.
|
||
//
|
||
|
||
if (Status == DLC_STATUS_NO_MEMORY) {
|
||
Status = DLC_STATUS_OUT_OF_RCV_BUFFERS;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcEventIndication(
|
||
IN PDLC_FILE_CONTEXT pFileContext,
|
||
IN PVOID hEventObject,
|
||
IN UINT Event,
|
||
IN PVOID pEventInformation,
|
||
IN ULONG SecondaryInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primitive handles all LLC and NDIS events
|
||
and translates them to DLC events, that are either immediately
|
||
executed by a pending (and matching) read command or
|
||
they are queued to the event queue.
|
||
LLC cannot provide any packet with these events, beacuse they
|
||
were not initiated by the protocol, but they just happened
|
||
asynchronously in the data link driver.
|
||
|
||
Special:
|
||
|
||
This routine must not call back the data link driver, if has
|
||
gon any other DLC status indication except INDICATE_CONNECT_REQUEST
|
||
(that may be closed by DLC, if there are no available station ids
|
||
on the sap).
|
||
|
||
Arguments:
|
||
|
||
pFileContext - DLC object handle or a file context of the event
|
||
hEventObject - DLC object handle or a file context of the event
|
||
Event - LLC event code. Usually it can be used directly as
|
||
a DLC event code
|
||
pEventInformation - information to DLC status change block
|
||
(or another pointer to some misc information)
|
||
SecondaryInformation - dword information used by some NDIS errors
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDLC_OBJECT pDlcObject;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE('E');
|
||
|
||
if (pFileContext->State != DLC_FILE_CONTEXT_OPEN) {
|
||
return;
|
||
}
|
||
|
||
ENTER_DLC(pFileContext);
|
||
|
||
//
|
||
// DLC status and NDIS adapter status events have different parameters,
|
||
// => we cannot do any common preprocessing for them.
|
||
//
|
||
|
||
switch (Event) {
|
||
case LLC_STATUS_CHANGE_ON_SAP:
|
||
Event = LLC_STATUS_CHANGE;
|
||
|
||
case LLC_STATUS_CHANGE:
|
||
pDlcObject = (PDLC_OBJECT)hEventObject;
|
||
|
||
#if LLC_DBG
|
||
|
||
if (pDlcObject != NULL && pDlcObject->State > DLC_OBJECT_CLOSED) {
|
||
DbgPrint("Invalid object type!");
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// We must create a DLC driver object, if a
|
||
// connect request has created a new link station
|
||
// in the data link driver.
|
||
//
|
||
|
||
if (SecondaryInfo & INDICATE_CONNECT_REQUEST) {
|
||
|
||
//
|
||
// Create the DLC driver object, if remote connection
|
||
// request created a new link station on LLC.
|
||
// The connect request may be done as well for
|
||
// a disconnected station.
|
||
//
|
||
|
||
if (pDlcObject->Type == DLC_SAP_OBJECT) {
|
||
|
||
NTSTATUS Status;
|
||
|
||
Status = InitializeLinkStation(
|
||
pFileContext,
|
||
pDlcObject, // Sap station id
|
||
NULL,
|
||
((PDLC_STATUS_TABLE)pEventInformation)->hLlcLinkStation,
|
||
&pDlcObject // NEW Link station id
|
||
);
|
||
if (Status != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// This client has all its available link stations
|
||
// reserved or we are simply run out of memory.
|
||
// Several LLC clients may share the same SAP.
|
||
// All remote connections are handled by the
|
||
// first client registered on the sap
|
||
// until it runs out of available link stations.
|
||
// LlcCloseStation for a link indicating connect request
|
||
// will redirect the connection request to the next
|
||
// possible LLC client having opened the same sap or
|
||
// deletes the link station, if there are no clients left
|
||
//
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
|
||
LlcCloseStation(
|
||
((PDLC_STATUS_TABLE)pEventInformation)->hLlcLinkStation,
|
||
NULL
|
||
);
|
||
|
||
ENTER_DLC(pFileContext);
|
||
|
||
break; // We have done it
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The remotely created link station may send also other indications
|
||
// than Connect, even if there is not yet a link station object
|
||
// created in DLC driver. We must skip all those events.
|
||
//
|
||
|
||
if (pDlcObject->Type == DLC_LINK_OBJECT) {
|
||
|
||
PDLC_EVENT pDlcEvent = pDlcObject->u.Link.pStatusEvent;
|
||
|
||
pDlcEvent->Event = Event;
|
||
pDlcEvent->StationId = pDlcObject->StationId;
|
||
pDlcEvent->pOwnerObject = pDlcObject;
|
||
pDlcEvent->pEventInformation = pEventInformation;
|
||
pDlcEvent->SecondaryInfo |= SecondaryInfo;
|
||
|
||
//
|
||
// The next pointer is reset whenever the status event
|
||
// packet is read and disconnected from the event queue.
|
||
//
|
||
|
||
if (pDlcEvent->LlcPacket.pNext == NULL) {
|
||
QueueDlcEvent(pFileContext, (PDLC_PACKET)pDlcEvent);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case NDIS_STATUS_RING_STATUS:
|
||
|
||
ASSERT ( IS_NDIS_RING_STATUS(SecondaryInfo) );
|
||
|
||
//
|
||
// The secondary information is directly the
|
||
// the network statys code as defined for
|
||
// ibm token-ring and dlc api!
|
||
//
|
||
|
||
Event = LLC_NETWORK_STATUS;
|
||
|
||
//
|
||
// This event should go to all READ having defined the
|
||
// the network status flag!
|
||
//
|
||
|
||
MakeDlcEvent(pFileContext,
|
||
Event,
|
||
(USHORT)(-1),
|
||
NULL,
|
||
pEventInformation,
|
||
NDIS_RING_STATUS_TO_DLC_RING_STATUS(SecondaryInfo),
|
||
FALSE
|
||
);
|
||
break;
|
||
|
||
case NDIS_STATUS_CLOSED:
|
||
|
||
//
|
||
// NDIS status closed is given only when the network
|
||
// administrator is for some reason unloading NDIS.
|
||
// Thus we must always return the 'System Action' error
|
||
// code ('04') with LLC_CRITICAL_ERROR, but
|
||
// we will add it later when all stations has been closed.
|
||
//
|
||
|
||
if (pFileContext->State != DLC_FILE_CONTEXT_CLOSED) {
|
||
pFileContext->State = DLC_FILE_CONTEXT_CLOSED;
|
||
CloseAllStations(
|
||
pFileContext,
|
||
NULL, // we don't have any command to complete
|
||
LLC_CRITICAL_EXCEPTION,
|
||
NULL,
|
||
NULL,
|
||
&pFileContext->ClosingPacket
|
||
);
|
||
}
|
||
break;
|
||
|
||
case LLC_TIMER_TICK_EVENT:
|
||
|
||
//
|
||
// This flag is used to limit the number of the failing expand
|
||
// operations for the buffer pool. We don't try to do it again
|
||
// for a while, if we cannot lock memory.
|
||
//
|
||
|
||
MemoryLockFailed = FALSE;
|
||
|
||
//
|
||
// We free the extra locked pages in the buffer pool once
|
||
// in five seconds. The unlocking takes some time, and we
|
||
// don't want to do it whenever a read command is executed
|
||
// (as we do with the expanding)
|
||
//
|
||
|
||
pFileContext->TimerTickCounter++;
|
||
if ((pFileContext->TimerTickCounter % 10) == 0 && pFileContext->hBufferPool != NULL) {
|
||
BufferPoolFreeExtraPages(
|
||
#if DBG
|
||
pFileContext,
|
||
#endif
|
||
(PDLC_BUFFER_POOL)pFileContext->hBufferPool
|
||
);
|
||
}
|
||
|
||
//
|
||
// Decrement the tick count of the first object in the timer queue
|
||
// (if there is any) and complete its all sequential commands
|
||
// having zero tickout.
|
||
//
|
||
|
||
if (pFileContext->pTimerQueue != NULL) {
|
||
pFileContext->pTimerQueue->Overlay.TimerTicks--;
|
||
|
||
while (pFileContext->pTimerQueue != NULL
|
||
&& pFileContext->pTimerQueue->Overlay.TimerTicks == 0) {
|
||
|
||
PDLC_COMMAND pCommand;
|
||
|
||
pCommand = pFileContext->pTimerQueue;
|
||
pFileContext->pTimerQueue = (PDLC_COMMAND)pCommand->LlcPacket.pNext;
|
||
|
||
#if LLC_DBG
|
||
|
||
pCommand->LlcPacket.pNext = NULL;
|
||
|
||
#endif
|
||
|
||
CompleteDlcCommand(pFileContext, 0, pCommand, STATUS_SUCCESS);
|
||
}
|
||
}
|
||
break;
|
||
|
||
#if LLC_DBG
|
||
|
||
default:
|
||
LlcInvalidObjectType();
|
||
break;
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcCommandCompletion(
|
||
IN PDLC_FILE_CONTEXT pFileContext,
|
||
IN PDLC_OBJECT pDlcObject,
|
||
IN PDLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine completes the asynchronous DLC operations: Transmit,
|
||
TransferData (for receive), LlcConnect and LlcDisconnect.
|
||
|
||
Arguments:
|
||
|
||
pFileContext - DLC process and adapter specific file context
|
||
pDlcObject - the object, that was assosiated with the command
|
||
pPacket - packet assosiated with the command
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDLC_PACKET pRootNode;
|
||
UINT Status;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE('B');
|
||
|
||
Status = (UINT)pPacket->LlcPacket.Data.Completion.Status;
|
||
|
||
ENTER_DLC(pFileContext);
|
||
|
||
#if LLC_DBG
|
||
|
||
if (pDlcObject != NULL && pDlcObject->State > DLC_OBJECT_CLOSED) {
|
||
DbgPrint("Invalid object type!");
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
switch (pPacket->LlcPacket.Data.Completion.CompletedCommand) {
|
||
case LLC_RECEIVE_COMPLETION:
|
||
|
||
//
|
||
// The receiving object may be different from the
|
||
// actual object given by data link.
|
||
// (this case should be moved to a subprocedure, that would
|
||
// be called directly from the receive data handler, if
|
||
// TransferData is executed synchronously (it always does it).
|
||
// That would save at least 100 instructions.)
|
||
//
|
||
|
||
DLC_TRACE('h');
|
||
|
||
pDlcObject = pPacket->Event.pOwnerObject;
|
||
pDlcObject->PendingLlcRequests--;
|
||
|
||
if (Status != STATUS_SUCCESS || pDlcObject->State != DLC_OBJECT_OPEN) {
|
||
|
||
//
|
||
// We must free the receive buffers, the packet
|
||
// will be deallocated in the end of this procedure.
|
||
//
|
||
|
||
BufferPoolDeallocateList(pFileContext->hBufferPool,
|
||
pPacket->Event.pEventInformation
|
||
);
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
} else {
|
||
if (pPacket->Event.Overlay.RcvReadOption != LLC_RCV_READ_INDIVIDUAL_FRAMES) {
|
||
|
||
//
|
||
// The received frames must be chained,
|
||
// add the new buffer header to the old
|
||
// link list if there is any, The buffers
|
||
// are saved to a circular link list to make
|
||
// possible to build easily the final link list in
|
||
// application address space.
|
||
//
|
||
|
||
if (pDlcObject->pReceiveEvent != NULL) {
|
||
|
||
//
|
||
// new: pPacket->Event.pEventInformation
|
||
// base: pDlcObject->pReceiveEvent->pEventInformation
|
||
// Operations when a new element is added to base:
|
||
//
|
||
|
||
//
|
||
// 1. new->next = base->next
|
||
//
|
||
|
||
((PDLC_BUFFER_HEADER)pPacket->Event.pEventInformation)->FrameBuffer.pNextFrame
|
||
= ((PDLC_BUFFER_HEADER)pDlcObject->pReceiveEvent->pEventInformation)->FrameBuffer.pNextFrame;
|
||
|
||
//
|
||
// 2. base->next = new
|
||
//
|
||
|
||
((PDLC_BUFFER_HEADER)pDlcObject->pReceiveEvent->pEventInformation)->FrameBuffer.pNextFrame
|
||
= (PDLC_BUFFER_HEADER)pPacket->Event.pEventInformation;
|
||
|
||
//
|
||
// 3. base = new
|
||
//
|
||
|
||
pDlcObject->pReceiveEvent->pEventInformation = pPacket->Event.pEventInformation;
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
//
|
||
// This event is already queued =>
|
||
// we may leave this procedure
|
||
//
|
||
|
||
break; // ********** EXIT ************
|
||
} else {
|
||
pDlcObject->pReceiveEvent = &pPacket->Event;
|
||
}
|
||
}
|
||
|
||
//
|
||
// All receives are events. The event is handled immediately
|
||
// if there is a pending command in the command queue,
|
||
// otherwise the command is queued to the event queue to be
|
||
// read by a command issued later.
|
||
//
|
||
|
||
pPacket->Event.Overlay.StationIdMask = (USHORT)(-1);
|
||
QueueDlcEvent(pFileContext, pPacket);
|
||
}
|
||
break;
|
||
|
||
case LLC_SEND_COMPLETION:
|
||
|
||
//
|
||
// We first free or/and unlock all buffers, that
|
||
// were used in the transmit of this frame.
|
||
//
|
||
|
||
DLC_TRACE('i');
|
||
|
||
BufferPoolFreeXmitBuffers(pFileContext->hBufferPool, pPacket);
|
||
|
||
//
|
||
// Reset the local busy states, if there is now enough
|
||
// buffers the receive the expected stuff for a link station.
|
||
//
|
||
|
||
if (!IsListEmpty(&pFileContext->FlowControlQueue)
|
||
&& pFileContext->hBufferPool != NULL
|
||
&& BufGetUncommittedSpace(pFileContext->hBufferPool) >= 0) {
|
||
ResetLocalBusyBufferStates(pFileContext);
|
||
}
|
||
|
||
//
|
||
// This code completes a transmit command.
|
||
// It releases all resources allocated for the transmit
|
||
// and completes the command, if this was the last
|
||
// transmit associated with it.
|
||
// Note:
|
||
// Single transmit command may consists of several frames.
|
||
// We must wait until all NDIS send requests have been completed
|
||
// before we can complete the command. That's why the first transmit
|
||
// node is also a root node. All transmit nodes have a reference
|
||
// to the root node.
|
||
// (why we incrment/decrement the object reference count separately
|
||
// for each frame, we could do it only once for a transmit command).
|
||
//
|
||
|
||
pDlcObject->PendingLlcRequests--;
|
||
pRootNode = pPacket->Node.pTransmitNode;
|
||
|
||
//
|
||
// Don't delete root node packet, we will need it to queue the
|
||
// command completion (if the command completion flag is used)
|
||
//
|
||
|
||
if (pPacket != pRootNode) {
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
}
|
||
|
||
//
|
||
// We will save and keep the first asynchronous error status
|
||
//
|
||
|
||
if (Status != STATUS_SUCCESS && pRootNode->Node.pIrp->IoStatus.Status == STATUS_SUCCESS) {
|
||
pRootNode->Node.pIrp->IoStatus.Status = Status;
|
||
}
|
||
|
||
pRootNode->Node.FrameCount--;
|
||
if (pRootNode->Node.FrameCount == 0) {
|
||
CompleteTransmitCommand(pFileContext,
|
||
pRootNode->Node.pIrp,
|
||
pDlcObject,
|
||
pRootNode
|
||
);
|
||
}
|
||
break;
|
||
|
||
case LLC_RESET_COMPLETION:
|
||
pPacket->ResetPacket.pClosingInfo->CloseCounter--;
|
||
if (pPacket->ResetPacket.pClosingInfo->CloseCounter == 0) {
|
||
CompleteCloseReset(pFileContext, pPacket->ResetPacket.pClosingInfo);
|
||
}
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
break;
|
||
|
||
case LLC_CONNECT_COMPLETION:
|
||
|
||
DLC_TRACE('e');
|
||
|
||
CompleteDlcCommand(pFileContext,
|
||
pDlcObject->StationId,
|
||
&pPacket->DlcCommand,
|
||
Status
|
||
);
|
||
pDlcObject->PendingLlcRequests--;
|
||
break;
|
||
|
||
case LLC_DISCONNECT_COMPLETION:
|
||
|
||
//
|
||
// The disconnect is in dlc driver always connected to
|
||
// the closing of the link station. We just continue
|
||
// the asynchronous command. Still this process (waiting
|
||
// the other side to ack to disconnect packet DISC)
|
||
// may be interrupted by an immediate close command
|
||
// (DLC.RESET or DIR.CLOSE.ADAPTER).
|
||
//
|
||
|
||
DLC_TRACE('g');
|
||
|
||
if (pDlcObject->LlcObjectExists == TRUE) {
|
||
pDlcObject->LlcObjectExists = FALSE;
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
|
||
LlcCloseStation(pDlcObject->hLlcObject, (PLLC_PACKET)pPacket);
|
||
|
||
ENTER_DLC(pFileContext);
|
||
|
||
DereferenceLlcObject(pDlcObject);
|
||
|
||
//
|
||
// We don't want to complete a dlc object twice.
|
||
//
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
|
||
return;
|
||
}
|
||
|
||
case LLC_CLOSE_COMPLETION:
|
||
|
||
//
|
||
// Just free the command packet and update the reference counter.
|
||
// The end of this procedure takes care of the rest.
|
||
//
|
||
|
||
DLC_TRACE('f');
|
||
|
||
pDlcObject->PendingLlcRequests--;
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
break;
|
||
|
||
#if LLC_DBG
|
||
|
||
default:
|
||
LlcInvalidObjectType();
|
||
break;
|
||
|
||
#endif
|
||
|
||
};
|
||
|
||
#if LLC_DBG
|
||
|
||
if (pDlcObject != NULL && pDlcObject->PendingLlcRequests < 0) {
|
||
DbgPrint("Error: PendingLlcRequests < 0!!!\n");
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// we can try to complete the close/reset only when there are no
|
||
// pending commands issued to LLC (and NDIS).
|
||
// The procedure will check, if there is still pending commands.
|
||
//
|
||
|
||
if (pDlcObject != NULL && pDlcObject->State != DLC_OBJECT_OPEN) {
|
||
CompleteCloseStation(pFileContext, pDlcObject);
|
||
}
|
||
|
||
LEAVE_DLC(pFileContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
CompleteTransmitCommand(
|
||
IN PDLC_FILE_CONTEXT pFileContext,
|
||
IN PIRP pIrp,
|
||
IN PDLC_OBJECT pChainObject,
|
||
IN PDLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine completes the DLC transmit command and optionally
|
||
chains its CCB(s) to the completion list.
|
||
The transmit read option defines, if the transmit commands
|
||
are chained or if each command is completed with a separate
|
||
READ- command.
|
||
|
||
Arguments:
|
||
|
||
pFileContext - DLC process and adapter specific file context
|
||
pIrp - Io- request packet of the completed command
|
||
pChainObject - the DLC object the packet(s) was transmitted from
|
||
pPacket - the orginal packet of the transmit command
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID pUserCcbPointer = NULL;
|
||
PNT_DLC_PARMS pDlcParms = (PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// MODMOD RLF 01/19/93
|
||
//
|
||
|
||
BOOLEAN queuePacket = FALSE;
|
||
PVOID pCcb;
|
||
ULONG eventFlags;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// MODMOD ends
|
||
//
|
||
|
||
pDlcParms->Async.Parms.Transmit.FrameStatus = 0xCC;
|
||
|
||
//
|
||
// Check if the transmit commands should be linked to the completion list
|
||
//
|
||
|
||
if (pDlcParms->Async.Ccb.CommandCompletionFlag != 0) {
|
||
|
||
//
|
||
// Are they linked together in the same completion event?
|
||
//
|
||
|
||
if (pDlcParms->Async.Parms.Transmit.XmitReadOption != LLC_COMPLETE_SINGLE_XMIT_FRAME) {
|
||
if (pDlcParms->Async.Parms.Transmit.XmitReadOption == LLC_CHAIN_XMIT_COMMANDS_ON_SAP
|
||
&& pChainObject->Type == DLC_LINK_OBJECT) {
|
||
pChainObject = pChainObject->u.Link.pSap;
|
||
}
|
||
pChainObject->ChainedTransmitCount++;
|
||
pUserCcbPointer = pChainObject->pPrevXmitCcbAddress;
|
||
pChainObject->pPrevXmitCcbAddress = pDlcParms->Async.Ccb.pCcbAddress;
|
||
|
||
//
|
||
// Make new event only for the first transmit completion
|
||
//
|
||
|
||
if (pChainObject->ChainedTransmitCount == 1) {
|
||
pChainObject->pFirstChainedCcbAddress = pDlcParms->Async.Ccb.pCcbAddress;
|
||
pPacket->Event.pOwnerObject = pChainObject;
|
||
} else {
|
||
|
||
//
|
||
// There is already a pending event for the
|
||
// this transmit command, we may free this one.
|
||
// The space & speed optimal code execution requires
|
||
// a shameful jump.
|
||
//
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
//
|
||
// MODMOD RLF 01/21/93
|
||
//
|
||
|
||
pCcb = pDlcParms->Async.Ccb.pCcbAddress;
|
||
|
||
//
|
||
// MODMOD ends
|
||
//
|
||
|
||
//
|
||
// ***** G-O-T-O ********
|
||
//
|
||
|
||
goto ThisIsA_SHAME;
|
||
}
|
||
} else {
|
||
pPacket->Event.pOwnerObject = NULL;
|
||
}
|
||
|
||
//
|
||
// MODMOD RLF 01/19/93
|
||
//
|
||
|
||
////
|
||
//// We translate the orginal transit packet to a new event packet
|
||
////
|
||
//
|
||
//pPacket->Event.Event = LLC_TRANSMIT_COMPLETION;
|
||
//pPacket->Event.StationId = (USHORT)pChainObject->StationId;
|
||
//pPacket->Event.pEventInformation = pDlcParms->Async.Ccb.pCcbAddress;
|
||
//pPacket->Event.SecondaryInfo = pDlcParms->Async.Ccb.CommandCompletionFlag;
|
||
//QueueDlcEvent(pFileContext, pPacket);
|
||
|
||
queuePacket = TRUE;
|
||
pCcb = pDlcParms->Async.Ccb.pCcbAddress;
|
||
eventFlags = pDlcParms->Async.Ccb.CommandCompletionFlag;
|
||
|
||
//
|
||
// MODMOD ends
|
||
//
|
||
|
||
} else {
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pPacket);
|
||
|
||
}
|
||
|
||
ThisIsA_SHAME:
|
||
|
||
//
|
||
// Set the default value to the returned frame status
|
||
//
|
||
|
||
if (pIrp->IoStatus.Status != STATUS_SUCCESS) {
|
||
|
||
//
|
||
// Set the FS (frame status) error code, NDIS has
|
||
// returned an FS-related error code.
|
||
// Note: This error status is never returned in the
|
||
// case of I- frames (or should it be?).
|
||
//
|
||
|
||
if (pIrp->IoStatus.Status == NDIS_STATUS_NOT_RECOGNIZED) {
|
||
pDlcParms->Async.Parms.Transmit.FrameStatus = 0;
|
||
pDlcParms->Async.Ccb.uchDlcStatus = LLC_STATUS_TRANSMIT_ERROR_FS;
|
||
} else if (pIrp->IoStatus.Status == NDIS_STATUS_NOT_COPIED) {
|
||
pDlcParms->Async.Parms.Transmit.FrameStatus = 0x44;
|
||
pDlcParms->Async.Ccb.uchDlcStatus = LLC_STATUS_TRANSMIT_ERROR_FS;
|
||
} else if (pIrp->IoStatus.Status == NDIS_STATUS_INVALID_PACKET) {
|
||
pDlcParms->Async.Parms.Transmit.FrameStatus = 0;
|
||
pDlcParms->Async.Ccb.uchDlcStatus = LLC_STATUS_INVALID_FRAME_LENGTH;
|
||
} else {
|
||
|
||
//
|
||
// Don't overwrite the existing DLC error codes!
|
||
//
|
||
|
||
if (pIrp->IoStatus.Status < DLC_STATUS_ERROR_BASE
|
||
|| pIrp->IoStatus.Status > DLC_STATUS_MAX_ERROR) {
|
||
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)LLC_STATUS_TRANSMIT_ERROR;
|
||
} else {
|
||
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)(pIrp->IoStatus.Status - DLC_STATUS_ERROR_BASE);
|
||
}
|
||
}
|
||
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
} else {
|
||
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)STATUS_SUCCESS;
|
||
}
|
||
|
||
pDlcParms->Async.Ccb.pCcbAddress = pUserCcbPointer;
|
||
|
||
//
|
||
// Copy the optional second output buffer to user memory.
|
||
//
|
||
|
||
if (IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.IoControlCode == IOCTL_DLC_TRANSMIT) {
|
||
|
||
//
|
||
// MODMOD RLF 01/21/93
|
||
//
|
||
// Transmit now uses METHOD_OUT_DIRECT which means we update the CCB
|
||
// with the pNext and uchDlcStatus fields
|
||
//
|
||
|
||
PLLC_CCB pInputCcb;
|
||
PUCHAR pFrameStatus;
|
||
|
||
pInputCcb = (PLLC_CCB)MmGetSystemAddressForMdl(pIrp->MdlAddress);
|
||
|
||
//
|
||
// the pointer may be an unaligned VDM pointer!
|
||
//
|
||
|
||
SmbPutUlong((PULONG)(&pInputCcb->pNext), (ULONG)pUserCcbPointer);
|
||
pInputCcb->uchDlcStatus = pDlcParms->Async.Ccb.uchDlcStatus;
|
||
|
||
//
|
||
// MODMOD ends
|
||
//
|
||
|
||
//
|
||
// performance (slight) improvement. The following copies A SINGLE BYTE
|
||
// (the frame status field). Replace call to copy routine with single
|
||
// byte move
|
||
//
|
||
|
||
//LlcMemCpy(MmGetSystemAddressForMdl((PMDL)pDlcParms->Async.Ccb.u.pMdl),
|
||
// &pDlcParms->Async.Parms.Transmit.FrameStatus,
|
||
// aSpecialOutputBuffers[IOCTL_DLC_TRANSMIT_INDEX]
|
||
// );
|
||
|
||
pFrameStatus = (PUCHAR)MmGetSystemAddressForMdl((PMDL)pDlcParms->Async.Ccb.u.pMdl);
|
||
*pFrameStatus = pDlcParms->Async.Parms.Transmit.FrameStatus;
|
||
|
||
UnlockAndFreeMdl(pDlcParms->Async.Ccb.u.pMdl);
|
||
}
|
||
|
||
//
|
||
// we are about to complete this IRP - remove the cancel routine
|
||
//
|
||
|
||
// RELEASE_DRIVER_LOCK();
|
||
|
||
SetIrpCancelRoutine(pIrp, FALSE);
|
||
IoCompleteRequest(pIrp, (CCHAR)IO_NETWORK_INCREMENT);
|
||
|
||
// ACQUIRE_DRIVER_LOCK();
|
||
|
||
//
|
||
// MODMOD RLF 01/19/93
|
||
//
|
||
// Moved queueing of the event until after the IoCompleteRequest because it
|
||
// was possible for the following to occur:
|
||
//
|
||
// Thread A:
|
||
//
|
||
// 1. app allocates transmit CCB & submits it. Transmit completion is to
|
||
// be picked up on a READ CCB
|
||
// 2. transmit completes
|
||
// 3. DLC queues event for transmit completion
|
||
//
|
||
// Thread B:
|
||
//
|
||
// 4. app submits READ CCB
|
||
// 5. READ finds completed transmit event on DLC event queue & removes it
|
||
// 6. READ IRP is completed (IoCompleteRequest)
|
||
// 7. app checks READ CCB and deallocates transmit CCB
|
||
// 8. app reallocates memory previously used for transmit CCB
|
||
//
|
||
// Thread A:
|
||
//
|
||
// 9. transmit IRP is completed (IoCompleteRequest)
|
||
//
|
||
// At this point, the IoCompleteRequest for the transmit copies some
|
||
// completion info over the area which used to be the original transmit CCB
|
||
// but has since been reallocated, causing lachrymae maximus
|
||
//
|
||
// This is safe because in this case we know we have a transmit which is
|
||
// destined to be picked up by a READ (its ulCompletionFlag parameter is
|
||
// non-zero), so it doesn't do any harm if we complete the IRP before
|
||
// queueing the event for a READ
|
||
//
|
||
|
||
if (queuePacket) {
|
||
pPacket->Event.Event = LLC_TRANSMIT_COMPLETION;
|
||
pPacket->Event.StationId = (USHORT)pChainObject->StationId;
|
||
pPacket->Event.pEventInformation = pCcb;
|
||
pPacket->Event.SecondaryInfo = eventFlags;
|
||
QueueDlcEvent(pFileContext, pPacket);
|
||
}
|
||
|
||
//
|
||
// MODMOD ends
|
||
//
|
||
|
||
DereferenceFileContext(pFileContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
CompleteDlcCommand(
|
||
IN PDLC_FILE_CONTEXT pFileContext,
|
||
IN USHORT StationId,
|
||
IN PDLC_COMMAND pDlcCommand,
|
||
IN UINT Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine completes the DLC command and optionally
|
||
saves its CCB(s) to the completion list, if the command
|
||
has a command completion flag.
|
||
|
||
Arguments:
|
||
|
||
pFileContext - DLC process and adapter specific file context
|
||
StationId - the station id of the completed command (0 for non station
|
||
based commands)
|
||
pDlcCommand - the caller must provide either command completion packet
|
||
or IRP
|
||
Status - command completion status
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID pCcbAddress;
|
||
ULONG CommandCompletionFlag;
|
||
PIRP pIrp;
|
||
|
||
pIrp = pDlcCommand->pIrp;
|
||
|
||
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pDlcCommand);
|
||
|
||
pCcbAddress = ((PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer)->Async.Ccb.pCcbAddress;
|
||
CommandCompletionFlag = ((PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer)->Async.Ccb.CommandCompletionFlag;
|
||
CompleteAsyncCommand(pFileContext, Status, pIrp, NULL, FALSE);
|
||
|
||
//
|
||
// Queue command completion event, if the command completion flag was set
|
||
//
|
||
|
||
if (CommandCompletionFlag != 0) {
|
||
MakeDlcEvent(pFileContext,
|
||
DLC_COMMAND_COMPLETION,
|
||
StationId,
|
||
NULL,
|
||
pCcbAddress,
|
||
CommandCompletionFlag,
|
||
FALSE
|
||
);
|
||
}
|
||
}
|