3381 lines
109 KiB
C
3381 lines
109 KiB
C
/*++
|
||
|
||
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
iframes.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines called to handle i-frames received
|
||
from the data link provider. Most of these routines are called at receive
|
||
indication time.
|
||
|
||
Also included here are routines that process data at receive completion
|
||
time. These are limited to handling DFM/DOL frames.
|
||
|
||
The following frame types are cracked by routines in this module:
|
||
|
||
o NBF_CMD_DATA_ACK
|
||
o NBF_CMD_DATA_FIRST_MIDDLE
|
||
o NBF_CMD_DATA_ONLY_LAST
|
||
o NBF_CMD_SESSION_CONFIRM
|
||
o NBF_CMD_SESSION_END
|
||
o NBF_CMD_SESSION_INITIALIZE
|
||
o NBF_CMD_NO_RECEIVE
|
||
o NBF_CMD_RECEIVE_OUTSTANDING
|
||
o NBF_CMD_RECEIVE_CONTINUE
|
||
o NBF_CMD_SESSION_ALIVE
|
||
|
||
Author:
|
||
|
||
David Beaver (dbeaver) 1-July-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode, DISPATCH_LEVEL.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
extern ULONG StartTimerDelayedAck;
|
||
|
||
#define NbfUsePiggybackAcks 1
|
||
#if DBG
|
||
extern ULONG NbfDebugPiggybackAcks;
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NbfAcknowledgeDataOnlyLast(
|
||
IN PTP_CONNECTION Connection,
|
||
IN ULONG MessageLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes care of acknowledging a DOL which has
|
||
been received. It either sends a DATA_ACK right away, or
|
||
queues a request for a piggyback ack.
|
||
|
||
NOTE: This routine is called with the connection spinlock
|
||
held, and it returns with it released. IT MUST BE CALLED
|
||
AT DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
MessageLength - the total length (including all DFMs and this
|
||
DOL) of the message.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
|
||
|
||
//
|
||
// Determine if we need to ack at all.
|
||
//
|
||
|
||
if (Connection->CurrentReceiveNoAck) {
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Determine if a piggyback ack is feasible.
|
||
//
|
||
if (NbfUsePiggybackAcks &&
|
||
Connection->CurrentReceiveAckQueueable) {
|
||
|
||
//
|
||
// The sender allows it, see if we want to.
|
||
//
|
||
|
||
#if 0
|
||
//
|
||
// First reset this variable, to be safe.
|
||
//
|
||
|
||
Connection->CurrentReceiveAckQueueable = FALSE;
|
||
#endif
|
||
|
||
//
|
||
// For long sends, don't bother since these
|
||
// often happen without back traffic.
|
||
//
|
||
|
||
if (MessageLength >= 8192L) {
|
||
#if DBG
|
||
if (NbfDebugPiggybackAcks) {
|
||
NbfPrint0("M");
|
||
}
|
||
#endif
|
||
goto NormalDataAck;
|
||
}
|
||
|
||
//
|
||
// If there have been two receives in a row with
|
||
// no sends in between, don't wait for back traffic.
|
||
//
|
||
|
||
if (Connection->ConsecutiveReceives >= 2) {
|
||
#if DBG
|
||
if (NbfDebugPiggybackAcks) {
|
||
NbfPrint0("R");
|
||
}
|
||
#endif
|
||
goto NormalDataAck;
|
||
}
|
||
|
||
//
|
||
// Do not put a stopping connection on the DataAckQueue
|
||
//
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) {
|
||
#if DBG
|
||
if (NbfDebugPiggybackAcks) {
|
||
NbfPrint0("S");
|
||
}
|
||
#endif
|
||
goto NormalDataAck;
|
||
}
|
||
|
||
//
|
||
// Queue the piggyback ack request. If the timer expires
|
||
// before a DFM or DOL is sent, a normal DATA ACK will
|
||
// be sent.
|
||
//
|
||
// Connection->Header.TransmitCorrelator has already been filled in.
|
||
//
|
||
|
||
//
|
||
// BAD! We shouldn't already have an ack queued.
|
||
//
|
||
|
||
ASSERT ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0);
|
||
|
||
KeQueryTickCount (&Connection->ConnectStartTime);
|
||
Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_ACK;
|
||
|
||
#if DBG
|
||
if (NbfDebugPiggybackAcks) {
|
||
NbfPrint0("Q");
|
||
}
|
||
#endif
|
||
|
||
DeviceContext = Connection->Link->Provider;
|
||
|
||
if (!Connection->OnDataAckQueue) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
||
|
||
if (!Connection->OnDataAckQueue) {
|
||
|
||
Connection->OnDataAckQueue = TRUE;
|
||
InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage);
|
||
|
||
if (!(DeviceContext->a.i.DataAckQueueActive)) {
|
||
|
||
StartTimerDelayedAck++;
|
||
NbfStartShortTimer (DeviceContext);
|
||
DeviceContext->a.i.DataAckQueueActive = TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
||
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
INCREMENT_COUNTER (DeviceContext, PiggybackAckQueued);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
NormalDataAck:;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
NbfSendDataAck (Connection);
|
||
|
||
} /* NbfAcknowledgeDataOnlyLast */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessSessionConfirm(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming SESSION_CONFIRM NetBIOS frame.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL cancelirql;
|
||
PLIST_ENTRY p;
|
||
PTP_REQUEST request;
|
||
PTDI_CONNECTION_INFORMATION remoteInformation;
|
||
USHORT HisMaxDataSize;
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION irpSp;
|
||
ULONG returnLength;
|
||
TA_NETBIOS_ADDRESS TempAddress;
|
||
// BOOLEAN TimerWasSet;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint1 ("ProcessSessionConfirm: Entered, Flags: %lx\n", Connection->Flags);
|
||
}
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
IoAcquireCancelSpinLock (&cancelirql);
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SC) != 0) {
|
||
Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SC;
|
||
|
||
//
|
||
// Get his capability bits and maximum frame size.
|
||
//
|
||
|
||
if (IFrame->Data1 & SESSION_CONFIRM_OPTIONS_20) {
|
||
Connection->Flags |= CONNECTION_FLAGS_VERSION2;
|
||
}
|
||
|
||
if (Connection->Link->Loopback) {
|
||
Connection->MaximumDataSize = 0x8000;
|
||
} else {
|
||
Connection->MaximumDataSize = (USHORT)
|
||
(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME));
|
||
|
||
HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256);
|
||
if (HisMaxDataSize < Connection->MaximumDataSize) {
|
||
Connection->MaximumDataSize = HisMaxDataSize;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Build a standard Netbios header for speed when sending
|
||
// data frames.
|
||
//
|
||
|
||
ConstructDataOnlyLast(
|
||
&Connection->NetbiosHeader,
|
||
FALSE,
|
||
(USHORT)0,
|
||
Connection->Lsn,
|
||
Connection->Rsn);
|
||
|
||
//
|
||
// Turn off the connection request timer if there is one, and set
|
||
// this connection's state to READY.
|
||
//
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_READY;
|
||
|
||
INCREMENT_COUNTER (Connection->Provider, OpenConnections);
|
||
|
||
//
|
||
// Record that the connect request has been successfully
|
||
// completed by TpCompleteRequest.
|
||
//
|
||
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED;
|
||
|
||
if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) {
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
Connection->IndicationInProgress = FALSE;
|
||
IoReleaseCancelSpinLock (cancelirql);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Complete the TdiConnect request.
|
||
//
|
||
|
||
p = RemoveHeadList (&Connection->InProgressRequest);
|
||
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
//
|
||
// Now complete the request and get out.
|
||
//
|
||
|
||
if (p == &Connection->InProgressRequest) {
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
IoReleaseCancelSpinLock (cancelirql);
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// We have a completed connection with a queued connect. Complete
|
||
// the connect.
|
||
//
|
||
|
||
request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
IoSetCancelRoutine(request->IoRequestPacket, NULL);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket);
|
||
remoteInformation =
|
||
((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation;
|
||
if (remoteInformation != NULL) {
|
||
try {
|
||
if (remoteInformation->RemoteAddressLength != 0) {
|
||
|
||
//
|
||
// Build a temporary TA_NETBIOS_ADDRESS, then
|
||
// copy over as many bytes as fit.
|
||
//
|
||
|
||
TdiBuildNetbiosAddress(
|
||
Connection->CalledAddress.NetbiosName,
|
||
(BOOLEAN)(Connection->CalledAddress.NetbiosNameType ==
|
||
TDI_ADDRESS_NETBIOS_TYPE_GROUP),
|
||
&TempAddress);
|
||
|
||
if (remoteInformation->RemoteAddressLength >=
|
||
sizeof (TA_NETBIOS_ADDRESS)) {
|
||
|
||
returnLength = sizeof(TA_NETBIOS_ADDRESS);
|
||
remoteInformation->RemoteAddressLength = returnLength;
|
||
|
||
} else {
|
||
|
||
returnLength = remoteInformation->RemoteAddressLength;
|
||
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
|
||
&TempAddress,
|
||
returnLength);
|
||
|
||
} else {
|
||
|
||
returnLength = 0;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
returnLength = 0;
|
||
status = GetExceptionCode ();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
status = STATUS_SUCCESS;
|
||
returnLength = 0;
|
||
|
||
}
|
||
|
||
if (status == STATUS_SUCCESS) {
|
||
|
||
if ((ULONG)Connection->Retries == Connection->Provider->NameQueryRetries) {
|
||
|
||
INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterNoRetry);
|
||
|
||
} else {
|
||
|
||
INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterRetry);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Don't clear this until now, so that the connection is all
|
||
// set up before we allow more indications.
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
NbfCompleteRequest (request, status, returnLength);
|
||
|
||
} else {
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessSessionConfirm */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessSessionEnd(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming SESSION_END NetBIOS frame.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT data2;
|
||
NTSTATUS StopStatus;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("ProcessSessionEnd: Entered.\n");
|
||
}
|
||
|
||
//
|
||
// Handle the error code in the Data2 field. Current protocol says
|
||
// if the field is 0, then this is a normal HANGUP.NCB operation.
|
||
// If the field is 1, then this is an abnormal session end, caused
|
||
// by something like a SEND.NCB timing out. Of course, new protocol
|
||
// may be added in the future, so we handle only these specific cases.
|
||
//
|
||
|
||
data2 = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256);
|
||
switch (data2) {
|
||
case 0:
|
||
case 1:
|
||
StopStatus = STATUS_REMOTE_DISCONNECT;
|
||
break;
|
||
|
||
default:
|
||
PANIC ("ProcessSessionEnd: frame not expected.\n");
|
||
StopStatus = STATUS_INVALID_NETWORK_RESPONSE;
|
||
}
|
||
#if DBG
|
||
if (NbfDisconnectDebug) {
|
||
STRING remoteName, localName;
|
||
remoteName.Length = NETBIOS_NAME_LENGTH - 1;
|
||
remoteName.Buffer = Connection->RemoteName;
|
||
localName.Length = NETBIOS_NAME_LENGTH - 1;
|
||
localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName;
|
||
NbfPrint3( "SessionEnd received for connection to %S from %S; reason %s\n",
|
||
&remoteName, &localName,
|
||
data2 == 0 ? "NORMAL" : data2 == 1 ? "ABORT" : "UNKNOWN" );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Run-down this connection.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint0 ("ProcessSessionEnd calling NbfStopConnection\n");
|
||
}
|
||
NbfStopConnection (Connection, StopStatus); // disconnected by the other end
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessSessionEnd */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessSessionInitialize(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming SESSION_INITIALIZE NetBIOS frame.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL cancelirql;
|
||
PLIST_ENTRY p;
|
||
PTP_REQUEST request;
|
||
PIO_STACK_LOCATION irpSp;
|
||
USHORT HisMaxDataSize;
|
||
ULONG returnLength;
|
||
PTDI_CONNECTION_INFORMATION remoteInformation;
|
||
NTSTATUS status;
|
||
TA_NETBIOS_ADDRESS TempAddress;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint1 ("ProcessSessionInitialize: Entered, Flags: %lx\n", Connection->Flags);
|
||
}
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) {
|
||
Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SI;
|
||
|
||
//
|
||
// Get his capability bits and maximum frame size.
|
||
//
|
||
|
||
if (IFrame->Data1 & SESSION_INIT_OPTIONS_20) {
|
||
Connection->Flags |= CONNECTION_FLAGS_VERSION2;
|
||
}
|
||
|
||
if (Connection->Link->Loopback) {
|
||
Connection->MaximumDataSize = 0x8000;
|
||
} else {
|
||
Connection->MaximumDataSize = (USHORT)
|
||
(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME));
|
||
|
||
HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256);
|
||
if (HisMaxDataSize < Connection->MaximumDataSize) {
|
||
Connection->MaximumDataSize = HisMaxDataSize;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Build a standard Netbios header for speed when sending
|
||
// data frames.
|
||
//
|
||
|
||
ConstructDataOnlyLast(
|
||
&Connection->NetbiosHeader,
|
||
FALSE,
|
||
(USHORT)0,
|
||
Connection->Lsn,
|
||
Connection->Rsn);
|
||
|
||
//
|
||
// Save his session initialize correlator so we can send it
|
||
// in the session confirm frame.
|
||
//
|
||
|
||
Connection->NetbiosHeader.TransmitCorrelator = RESPONSE_CORR(IFrame);
|
||
|
||
//
|
||
// Turn off the connection request timer if there is one (we're done).
|
||
// Do this with the lock held in case the connection is about to
|
||
// be closed, to not interfere with the timer started when the
|
||
// connection started then.
|
||
//
|
||
|
||
if (KeCancelTimer (&Connection->Timer)) {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
NbfDereferenceConnection ("Timer canceled", Connection, CREF_TIMER); // remove timer reference.
|
||
|
||
} else {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
}
|
||
|
||
//
|
||
// Now, complete the listen request on the connection (if there was
|
||
// one) and continue with as much of the protocol request as possible.
|
||
// if the user has "pre-accepted" the connection, we'll just continue
|
||
// onward here and complete the entire connection setup. If the user
|
||
// was indicated and has not yet accepted, we'll just put the
|
||
// connection into the proper state and fall out the bottom without
|
||
// completing anything.
|
||
//
|
||
|
||
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
if (((Connection->Flags2 & CONNECTION_FLAGS2_ACCEPTED) != 0) ||
|
||
((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) != 0)) {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_SETUP) {
|
||
NbfPrint1("SessionInitialize: Accepted connection %lx\n", Connection);
|
||
}
|
||
//
|
||
// we've already accepted the connection; allow it to proceed.
|
||
// this is the normal path for kernel mode indication clients,
|
||
// or for those who don't specify TDI_QUERY_ACCEPT on the listen.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
Connection->Flags |= CONNECTION_FLAGS_READY;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
INCREMENT_COUNTER (Connection->Provider, OpenConnections);
|
||
|
||
//
|
||
// Record that the listen request has been successfully
|
||
// completed by NbfCompleteRequest.
|
||
//
|
||
|
||
Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED;
|
||
|
||
status = STATUS_SUCCESS;
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
NbfSendSessionConfirm (Connection);
|
||
|
||
} else {
|
||
|
||
if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) {
|
||
|
||
//
|
||
// we disconnected, destroy the connection
|
||
//
|
||
IF_NBFDBG (NBF_DEBUG_SETUP) {
|
||
NbfPrint1("SessionInitialize: Disconnected connection %lx\n", Connection);
|
||
}
|
||
|
||
status = STATUS_LOCAL_DISCONNECT;
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT);
|
||
|
||
} else {
|
||
|
||
//
|
||
// we've done nothing, wait for the user to accept on this
|
||
// connection. This is the "normal" path for non-indication
|
||
// clients.
|
||
//
|
||
|
||
Connection->Flags2 |= CONNECTION_FLAGS2_WAITING_SC;
|
||
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now, if there was no queued listen, we have done everything we can
|
||
// for this connection, so we simply exit and leave everything up to
|
||
// the user. If we've gotten an indication response that allows the
|
||
// connection to proceed, we will come out of here with a connection
|
||
// that's up and running.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock (&cancelirql);
|
||
ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
p = RemoveHeadList (&Connection->InProgressRequest);
|
||
if (p == &Connection->InProgressRequest) {
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
IoReleaseCancelSpinLock (cancelirql);
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// We have a completed connection with a queued listen. Complete
|
||
// the listen and let the user do an accept at some time down the
|
||
// road.
|
||
//
|
||
|
||
RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
|
||
|
||
request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
IoSetCancelRoutine(request->IoRequestPacket, NULL);
|
||
IoReleaseCancelSpinLock (cancelirql);
|
||
|
||
irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket);
|
||
remoteInformation =
|
||
((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation;
|
||
if (remoteInformation != NULL) {
|
||
try {
|
||
if (remoteInformation->RemoteAddressLength != 0) {
|
||
|
||
//
|
||
// Build a temporary TA_NETBIOS_ADDRESS, then
|
||
// copy over as many bytes as fit.
|
||
//
|
||
|
||
TdiBuildNetbiosAddress(
|
||
Connection->CalledAddress.NetbiosName,
|
||
(BOOLEAN)(Connection->CalledAddress.NetbiosNameType ==
|
||
TDI_ADDRESS_NETBIOS_TYPE_GROUP),
|
||
&TempAddress);
|
||
|
||
if (remoteInformation->RemoteAddressLength >=
|
||
sizeof (TA_NETBIOS_ADDRESS)) {
|
||
|
||
returnLength = sizeof(TA_NETBIOS_ADDRESS);
|
||
remoteInformation->RemoteAddressLength = returnLength;
|
||
|
||
} else {
|
||
|
||
returnLength = remoteInformation->RemoteAddressLength;
|
||
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
|
||
&TempAddress,
|
||
returnLength);
|
||
|
||
} else {
|
||
|
||
returnLength = 0;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
returnLength = 0;
|
||
status = GetExceptionCode ();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
status = STATUS_SUCCESS;
|
||
returnLength = 0;
|
||
|
||
}
|
||
|
||
//
|
||
// Don't clear this until now, so that the connection is all
|
||
// set up before we allow more indications.
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
NbfCompleteRequest (request, status, 0);
|
||
|
||
} else {
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
#if DBG
|
||
NbfPrint3 ("ProcessSessionInitialize: C %lx, Flags %lx, Flags2 %lx\n",
|
||
Connection, Connection->Flags, Connection->Flags2);
|
||
#endif
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessSessionInitialize */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessNoReceive(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming NO_RECEIVE NetBIOS frame.
|
||
|
||
NOTE: This routine is called with the connection spinlock
|
||
held and returns with it released.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("ProcessNoReceive: Entered.\n");
|
||
}
|
||
|
||
switch (Connection->SendState) {
|
||
case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet.
|
||
case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized.
|
||
case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions.
|
||
case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR).
|
||
case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK.
|
||
// Connection->SendState = CONNECTION_SENDSTATE_W_RCVCONT;
|
||
//
|
||
// this used to be here, and is right for the other side of the connection. It's
|
||
// wrong here.
|
||
// Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256);
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
break;
|
||
|
||
case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE.
|
||
case CONNECTION_SENDSTATE_IDLE: // no sends being processed.
|
||
PANIC ("ProcessNoReceive: Frame not expected.\n");
|
||
break;
|
||
|
||
default:
|
||
PANIC ("ProcessNoReceive: Invalid SendState.\n");
|
||
}
|
||
|
||
//
|
||
// Don't clear this until ReframeSend has been called
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessNoReceive */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessReceiveOutstanding(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming RECEIVE_OUTSTANDING NetBIOS frame.
|
||
|
||
NOTE: This routine is called with the connection spinlock
|
||
held and returns with it released.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("ProcessReceiveOutstanding: Entered.\n");
|
||
}
|
||
|
||
switch (Connection->SendState) {
|
||
case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet.
|
||
case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized.
|
||
case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions.
|
||
case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR).
|
||
case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK.
|
||
case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE.
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256);
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
if ((Connection->Flags & CONNECTION_FLAGS_READY) != 0) {
|
||
Connection->Flags |= CONNECTION_FLAGS_RESYNCHING;
|
||
Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
||
}
|
||
break;
|
||
|
||
case CONNECTION_SENDSTATE_IDLE: // no sends being processed.
|
||
PANIC ("ProcessReceiveOutstanding: Frame not expected.\n");
|
||
break;
|
||
|
||
default:
|
||
PANIC ("ProcessReceiveOutstanding: Invalid SendState.\n");
|
||
}
|
||
|
||
//
|
||
// Don't clear this until ReframeSend has been called
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
//
|
||
// Now start packetizing the connection again since we've reframed
|
||
// the current send. If we were idle or in a bad state, then the
|
||
// packetizing routine will detect that.
|
||
//
|
||
// *** StartPacketizingConnection releases the Connection spin lock.
|
||
//
|
||
|
||
StartPacketizingConnection (Connection, FALSE);
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessReceiveOutstanding */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessReceiveContinue(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming RECEIVE_CONTINUE NetBIOS frame.
|
||
|
||
NOTE: This routine is called with the connection spinlock
|
||
held and returns with it released.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("ProcessReceiveContinue: Entered.\n");
|
||
}
|
||
|
||
switch (Connection->SendState) {
|
||
case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet.
|
||
case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized.
|
||
case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions.
|
||
case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR).
|
||
case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK.
|
||
case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE.
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
ReframeSend (Connection, Connection->sp.MessageBytesSent);
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
Connection->Flags |= CONNECTION_FLAGS_RESYNCHING;
|
||
Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
||
break;
|
||
|
||
case CONNECTION_SENDSTATE_IDLE: // no sends being processed.
|
||
PANIC ("ProcessReceiveContinue: Frame not expected.\n");
|
||
break;
|
||
|
||
default:
|
||
PANIC ("ProcessReceiveContinue: Invalid SendState.\n");
|
||
}
|
||
|
||
//
|
||
// Don't clear this until ReframeSend has been called
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
//
|
||
// Now start packetizing the connection again since we've reframed
|
||
// the current send. If we were idle or in a bad state, then the
|
||
// packetizing routine will detect that.
|
||
//
|
||
// *** StartPacketizingConnection releases the Connection spin lock.
|
||
//
|
||
|
||
StartPacketizingConnection (Connection, FALSE);
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessReceiveContinue */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessSessionAlive(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PNBF_HDR_CONNECTION IFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles an incoming SESSION_ALIVE NetBIOS frame. This
|
||
routine is by far the simplest in the transport because it does nothing.
|
||
The SESSION_ALIVE frame is simply a dummy frame that is sent on the
|
||
reliable data link layer to determine if the data link is still active;
|
||
no NetBIOS level protocol processing is performed.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a transport connection (TP_CONNECTION).
|
||
|
||
IFrame - Pointer to NetBIOS connection-oriented header.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER (Connection); // prevent compiler warnings
|
||
UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("ProcessSessionAlive: Entered.\n");
|
||
}
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
} /* ProcessSessionAlive */
|
||
|
||
|
||
VOID
|
||
NbfProcessIIndicate(
|
||
IN BOOLEAN Command,
|
||
IN BOOLEAN PollFinal,
|
||
IN PTP_LINK Link,
|
||
IN PUCHAR DlcHeader,
|
||
IN UINT DlcIndicatedLength,
|
||
IN UINT DlcTotalLength,
|
||
IN NDIS_HANDLE ReceiveContext,
|
||
IN BOOLEAN Loopback
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes a received I frame at indication time. It will do
|
||
all necessary verification processing of the frame and pass those frames
|
||
that are valid on to the proper handling routines.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Command - Boolean set to TRUE if command, else FALSE if response.
|
||
|
||
PollFinal - Boolean set to TRUE if Poll or Final.
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Header - Pointer to a DLC I-type frame.
|
||
|
||
DlcHeader - A pointer to the start of the DLC header in the packet.
|
||
|
||
DlcIndicatedLength - The length of the packet indicated, starting at
|
||
DlcHeader.
|
||
|
||
DlcTotalLength - The total length of the packet, starting at DlcHeader.
|
||
|
||
ReceiveContext - A magic value for NDIS that indicates which packet we're
|
||
talking about.
|
||
|
||
Loopback - Is this a loopback indication; used to determine whether
|
||
to call NdisTransferData or NbfTransferLoopbackData.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if DBG
|
||
UCHAR *s;
|
||
#endif
|
||
PNBF_HDR_CONNECTION nbfHeader;
|
||
PDLC_I_FRAME header;
|
||
NTSTATUS Status;
|
||
UCHAR lsn, rsn;
|
||
PTP_CONNECTION connection;
|
||
PUCHAR DataHeader;
|
||
ULONG DataTotalLength;
|
||
PLIST_ENTRY p;
|
||
BOOLEAN ConnectionFound;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 (" NbfProcessIIndicate: Entered.\n");
|
||
}
|
||
|
||
//
|
||
// Process any of: I-x/x.
|
||
//
|
||
|
||
header = (PDLC_I_FRAME)DlcHeader;
|
||
nbfHeader = (PNBF_HDR_CONNECTION)((PUCHAR)header + 4); // skip DLC hdr.
|
||
|
||
//
|
||
// Verify signatures. We test the signature as a 16-bit
|
||
// word as specified in the NetBIOS Formats and Protocols manual,
|
||
// with the assert guarding us against big-endian systems.
|
||
//
|
||
|
||
ASSERT ((((PUCHAR)(&nbfHeader->Length))[0] + ((PUCHAR)(&nbfHeader->Length))[1]*256) ==
|
||
HEADER_LENGTH(nbfHeader));
|
||
|
||
if (HEADER_LENGTH(nbfHeader) != sizeof(NBF_HDR_CONNECTION)) {
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Too short or long.\n");
|
||
}
|
||
return; // frame too small or too large.
|
||
}
|
||
|
||
if (HEADER_SIGNATURE(nbfHeader) != NETBIOS_SIGNATURE) {
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Signature bad.\n");
|
||
}
|
||
return; // invalid signature in frame.
|
||
}
|
||
|
||
DataHeader = (PUCHAR)DlcHeader + (4 + sizeof(NBF_HDR_CONNECTION));
|
||
DataTotalLength = DlcTotalLength - (4 + sizeof(NBF_HDR_CONNECTION));
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable
|
||
|
||
switch (Link->State) {
|
||
|
||
case LINK_STATE_READY:
|
||
|
||
//
|
||
// Link is balanced. This code is extremely critical since
|
||
// it is the most-covered path in the system for small and
|
||
// large I/O. Be very careful in adding code here as it will
|
||
// seriously affect the overall performance of the LAN. It is
|
||
// first in the list of possible states for that reason.
|
||
//
|
||
|
||
#if DBG
|
||
s = "READY";
|
||
#endif
|
||
Link->LinkBusy = FALSE;
|
||
|
||
//
|
||
// The I-frame's N(S) should match our V(R). If it
|
||
// doesn't, issue a reject. Otherwise, increment our V(R).
|
||
//
|
||
|
||
if ((UCHAR)((header->SendSeq >> 1) & 0x7F) != Link->NextReceive) {
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 (" NbfProcessIIndicate: N(S) != V(R).\n");
|
||
}
|
||
|
||
if (Link->ReceiveState == RECEIVE_STATE_REJECTING) {
|
||
|
||
|
||
//
|
||
// We already sent a reject, only respond if
|
||
// he is polling.
|
||
//
|
||
|
||
if (Command & PollFinal) {
|
||
NbfSendRr(Link, FALSE, TRUE); // releases lock
|
||
} else {
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
}
|
||
|
||
} else {
|
||
|
||
Link->ReceiveState = RECEIVE_STATE_REJECTING;
|
||
|
||
//
|
||
// NbfSendRej releases the spinlock.
|
||
//
|
||
|
||
if (Command) {
|
||
NbfSendRej (Link, FALSE, PollFinal);
|
||
} else {
|
||
NbfSendRej (Link, FALSE, FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Update our "bytes rejected" counters.
|
||
//
|
||
|
||
ADD_TO_LARGE_INTEGER(
|
||
&Link->Provider->Statistics.DataFrameBytesRejected,
|
||
DataTotalLength);
|
||
++Link->Provider->Statistics.DataFramesRejected;
|
||
|
||
//
|
||
// Discard this packet.
|
||
//
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Find the transport connection object associated with this frame.
|
||
// Because there may be several NetBIOS (transport) connections
|
||
// over the same data link connection, and the ConnectionContext
|
||
// value represents a data link connection to a specific address,
|
||
// we simply use the RSN field in the frame to index into the
|
||
// connection database for this link object.
|
||
//
|
||
// We do this before processing the rest of the LLC header,
|
||
// in case the connection is busy and we have to ignore
|
||
// the frame.
|
||
//
|
||
|
||
ConnectionFound = FALSE;
|
||
|
||
lsn = nbfHeader->DestinationSessionNumber;
|
||
rsn = nbfHeader->SourceSessionNumber;
|
||
|
||
if ((lsn == 0) || (lsn > NETBIOS_SESSION_LIMIT)) {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("NbfProcessIIndicate: Invalid LSN.\n");
|
||
}
|
||
|
||
} else {
|
||
|
||
p = Link->ConnectionDatabase.Flink;
|
||
while (p != &Link->ConnectionDatabase) {
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if (connection->Lsn >= lsn) { // assumes ordered list
|
||
break;
|
||
}
|
||
p = p->Flink;
|
||
}
|
||
|
||
// Don't use compound if 'cause Connection may be garbage
|
||
|
||
if (p == &Link->ConnectionDatabase) {
|
||
#if DBG
|
||
NbfPrint2 ("NbfProcessIIndicate: Connection not found in database: \n Lsn %x Link %lx",
|
||
lsn, Link);
|
||
NbfPrint6 ("Remote: %x-%x-%x-%x-%x-%x\n",
|
||
Link->HardwareAddress.Address[0], Link->HardwareAddress.Address[1],
|
||
Link->HardwareAddress.Address[2], Link->HardwareAddress.Address[3],
|
||
Link->HardwareAddress.Address[4], Link->HardwareAddress.Address[5]);
|
||
#endif
|
||
} else if (connection->Lsn != lsn) {
|
||
#if DBG
|
||
NbfPrint0 ("NbfProcessIIndicate: Connection in database doesn't match.\n");
|
||
#endif
|
||
} else if (connection->Rsn != rsn) {
|
||
#if DBG
|
||
NbfPrint3 ("NbfProcessIIndicate: Connection lsn %d had rsn %d, got frame for %d\n",
|
||
connection->Lsn, connection->Rsn, rsn);
|
||
#endif
|
||
} else {
|
||
|
||
//
|
||
// The connection is good, proceed.
|
||
//
|
||
|
||
ConnectionFound = TRUE;
|
||
|
||
if (connection->IndicationInProgress) {
|
||
NbfPrint1("ProcessIIndicate: Indication in progress on %lx\n", connection);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set this, it prevents other I-frames from being received
|
||
// on this connection. The various ProcessXXX routines
|
||
// that are called from the switch below will clear
|
||
// this flag when they determine it is OK to be reentered.
|
||
//
|
||
|
||
connection->IndicationInProgress = TRUE;
|
||
|
||
|
||
// This reference is removed before this function returns or
|
||
// we are done with the LINK_STATE_READY part of the outer switch.
|
||
|
||
NbfReferenceConnection ("Processing IFrame", connection, CREF_PROCESS_DATA);
|
||
|
||
|
||
}
|
||
|
||
}
|
||
|
||
#if PKT_LOG
|
||
if (ConnectionFound) {
|
||
// We have the connection here, log the packet for debugging
|
||
NbfLogRcvPacket(connection,
|
||
NULL,
|
||
DlcHeader,
|
||
DlcTotalLength,
|
||
DlcIndicatedLength);
|
||
}
|
||
else {
|
||
// We just have the link here, log the packet for debugging
|
||
NbfLogRcvPacket(NULL,
|
||
Link,
|
||
DlcHeader,
|
||
DlcTotalLength,
|
||
DlcIndicatedLength);
|
||
|
||
}
|
||
#endif // PKT_LOG
|
||
|
||
//
|
||
// As long as we don't have to drop this frame, adjust the link
|
||
// state correctly. If ConnectionFound is FALSE, then we exit
|
||
// right after doing this.
|
||
//
|
||
|
||
|
||
//
|
||
// The I-frame we expected arrived, clear rejecting state.
|
||
//
|
||
|
||
if (Link->ReceiveState == RECEIVE_STATE_REJECTING) {
|
||
Link->ReceiveState = RECEIVE_STATE_READY;
|
||
}
|
||
|
||
Link->NextReceive = (UCHAR)((Link->NextReceive+1) & 0x7f);
|
||
|
||
//
|
||
// If he is checkpointing, we need to respond with RR-c/f. If
|
||
// we respond, then stop the delayed ack timer. Otherwise, we
|
||
// need to start it because this is an I-frame that will not be
|
||
// acked immediately.
|
||
//
|
||
|
||
if (Command && PollFinal) {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 (" NbfProcessI: he's checkpointing.\n");
|
||
}
|
||
Link->RemoteNoPoll = FALSE;
|
||
StopT2 (Link); // we're acking, so no delay req'd.
|
||
NbfSendRr (Link, FALSE, TRUE); // releases lock
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
} else {
|
||
|
||
if (Link->RemoteNoPoll) {
|
||
|
||
if ((++Link->ConsecutiveIFrames) == Link->Provider->MaxConsecutiveIFrames) {
|
||
|
||
//
|
||
// This appears to be one of those remotes which
|
||
// never polls, so we send an RR if there are two
|
||
// frames outstanding (StopT2 sets ConsecutiveIFrames
|
||
// to 0).
|
||
//
|
||
|
||
StopT2 (Link); // we're acking, so no delay req'd.
|
||
NbfSendRr (Link, FALSE, FALSE); // releases lock
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
} else {
|
||
|
||
StartT2 (Link);
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->Provider->Interlock);
|
||
if (!Link->OnDeferredRrQueue) {
|
||
InsertTailList(
|
||
&Link->Provider->DeferredRrQueue,
|
||
&Link->DeferredRrLinkage);
|
||
Link->OnDeferredRrQueue = TRUE;
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (&Link->Provider->Interlock);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
StartT2 (Link); // start delayed ack sequence.
|
||
}
|
||
|
||
//
|
||
// If he is responding to a checkpoint, we need to clear our
|
||
// send state. Any packets which are still waiting for acknowlegement
|
||
// at this point must now be resent.
|
||
//
|
||
|
||
if ((!Command) && PollFinal) {
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 (" NbfProcessI: he's responding to our checkpoint.\n");
|
||
}
|
||
if (Link->SendState != SEND_STATE_CHECKPOINTING) {
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint1 (" NbfProcessI: Ckpt but SendState=%ld.\n",
|
||
Link->SendState);
|
||
}
|
||
}
|
||
StopT1 (Link); // checkpoint completed.
|
||
Link->SendState = SEND_STATE_READY;
|
||
StartTi (Link);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now, if we could not find the connection or the sequence
|
||
// numbers did not match, return. We don't call ResendLlcPackets
|
||
// in this case, but that is OK (eventually we will poll).
|
||
//
|
||
|
||
if (!ConnectionFound) {
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
return;
|
||
}
|
||
|
||
ASSERT (connection->LinkSpinLock == &Link->SpinLock);
|
||
|
||
//
|
||
// The N(R) in this frame may acknowlege some WackQ packets.
|
||
// We delay checking this until after processing the I-frame,
|
||
// so that we can get IndicationInProgress set to FALSE
|
||
// before we start resending the WackQ.
|
||
//
|
||
|
||
switch (nbfHeader->Command) {
|
||
|
||
case NBF_CMD_DATA_FIRST_MIDDLE:
|
||
case NBF_CMD_DATA_ONLY_LAST:
|
||
|
||
//
|
||
// First see if this packet has a piggyback ack -- we process
|
||
// this even if we throw the packet away below.
|
||
//
|
||
// This is a bit ugly since theoretically the piggyback
|
||
// ack bits in a DFM and a DOL could be different, but
|
||
// they aren't.
|
||
//
|
||
if (NbfUsePiggybackAcks) {
|
||
ASSERT (DFM_OPTIONS_ACK_INCLUDED == DOL_OPTIONS_ACK_INCLUDED);
|
||
|
||
if ((nbfHeader->Data1 & DFM_OPTIONS_ACK_INCLUDED) != 0) {
|
||
|
||
//
|
||
// This returns with the connection spinlock held
|
||
// but may release it and reacquire it.
|
||
//
|
||
|
||
CompleteSend(
|
||
connection,
|
||
TRANSMIT_CORR(nbfHeader));
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// NOTE: The connection spinlock is held here.
|
||
//
|
||
|
||
//
|
||
// If the connection is not ready, drop the frame.
|
||
//
|
||
|
||
if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) {
|
||
connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto SkipProcessIndicateData;
|
||
}
|
||
|
||
//
|
||
// A quick check for the three flags that are
|
||
// rarely set.
|
||
//
|
||
|
||
if ((connection->Flags & (CONNECTION_FLAGS_W_RESYNCH |
|
||
CONNECTION_FLAGS_RC_PENDING |
|
||
CONNECTION_FLAGS_RECEIVE_WAKEUP)) == 0) {
|
||
goto NoFlagsSet;
|
||
}
|
||
|
||
//
|
||
// If we are waiting for a resynch bit to be set in an
|
||
// incoming frame, toss the frame if it isn't set.
|
||
// Otherwise, clear the wait condition.
|
||
//
|
||
|
||
if (connection->Flags & CONNECTION_FLAGS_W_RESYNCH) {
|
||
if ((nbfHeader->Data2Low == 1) && (nbfHeader->Data2High == 0)) {
|
||
connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH;
|
||
} else {
|
||
connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, waiting for resynch.\n");
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto SkipProcessIndicateData;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have a previous receive that is pending
|
||
// completion, then we need to ignore this frame.
|
||
// This may be common on MP, so rather than drop
|
||
// it and wait for a poll, we send a NO_RECEIVE,
|
||
// then a RCV_OUTSTANDING when we have some
|
||
// resources.
|
||
//
|
||
|
||
if (connection->Flags & CONNECTION_FLAGS_RC_PENDING) {
|
||
|
||
//
|
||
// Hack the connection object so the NO_RECEIVE
|
||
// looks right.
|
||
//
|
||
|
||
connection->MessageBytesReceived = 0;
|
||
connection->MessageBytesAcked = 0;
|
||
connection->MessageInitAccepted = 0;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
NbfSendNoReceive (connection);
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
//
|
||
// We now turn on the PEND_INDICATE flag to show
|
||
// that we need to send RCV_OUTSTANDING when the
|
||
// receive completes. If RC_PENDING is now off,
|
||
// it means the receive was just completed, so
|
||
// we ourselves need to send the RCV_OUTSTANDING.
|
||
//
|
||
|
||
if ((connection->Flags & CONNECTION_FLAGS_RC_PENDING) == 0) {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
NbfSendReceiveOutstanding (connection);
|
||
ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
} else {
|
||
|
||
connection->Flags |= CONNECTION_FLAGS_PEND_INDICATE;
|
||
|
||
}
|
||
|
||
connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
IF_NBFDBG (NBF_DEBUG_IFRAMES) {
|
||
NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, receive complete pending.\n");
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto SkipProcessIndicateData;
|
||
}
|
||
|
||
//
|
||
// If we are discarding data received on this connection
|
||
// because we've sent a no receive, ditch it.
|
||
//
|
||
|
||
if (connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) {
|
||
connection->IndicationInProgress = FALSE;
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 ("NbfProcessIIndicate: In wakeup state, discarding frame.\n");
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto SkipProcessIndicateData;
|
||
}
|
||
|
||
NoFlagsSet:;
|
||
|
||
//
|
||
// The connection spinlock is held here.
|
||
//
|
||
|
||
if (nbfHeader->Command == NBF_CMD_DATA_FIRST_MIDDLE) {
|
||
|
||
//
|
||
// NOTE: This release connection->LinkSpinLock.
|
||
//
|
||
|
||
Status = ProcessIndicateData (
|
||
connection,
|
||
DlcHeader,
|
||
DlcIndicatedLength,
|
||
DataHeader,
|
||
DataTotalLength,
|
||
ReceiveContext,
|
||
FALSE,
|
||
Loopback);
|
||
|
||
//
|
||
// If the receive-continue bit is set in this frame, then we must
|
||
// reply with a RECEIVE_CONTINUE frame saying that he can continue
|
||
// sending. This old protocol option allowed a sender to send a
|
||
// single frame over to see if there was a receive posted before
|
||
// sending the entire message and potentially dropping the entire
|
||
// message. Because the TDI is indication-based, we cannot know
|
||
// if there is NO receive available until we actually try perform
|
||
// the indication, so we simply say that there is one posted.
|
||
// (This will only happen on DFMs.)
|
||
//
|
||
|
||
if (nbfHeader->Data1 & 0x01) {
|
||
|
||
//
|
||
// Save this to use in RECEIVE_CONTINUE.
|
||
//
|
||
|
||
connection->NetbiosHeader.TransmitCorrelator =
|
||
RESPONSE_CORR(nbfHeader);
|
||
|
||
NbfSendReceiveContinue (connection);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Keep track of how many consecutive receives we have had.
|
||
//
|
||
|
||
connection->ConsecutiveReceives++;
|
||
connection->ConsecutiveSends = 0;
|
||
|
||
//
|
||
// Save this information now, it will be needed
|
||
// when the ACK for this DOL is sent.
|
||
//
|
||
|
||
connection->CurrentReceiveAckQueueable =
|
||
(nbfHeader->Data1 & DOL_OPTIONS_ACK_W_DATA_ALLOWED);
|
||
|
||
connection->CurrentReceiveNoAck =
|
||
(nbfHeader->Data1 & DOL_OPTIONS_NO_ACK);
|
||
|
||
connection->NetbiosHeader.TransmitCorrelator =
|
||
RESPONSE_CORR(nbfHeader);
|
||
|
||
//
|
||
// NOTE: This release connection->LinkSpinLock.
|
||
//
|
||
|
||
Status = ProcessIndicateData (
|
||
connection,
|
||
DlcHeader,
|
||
DlcIndicatedLength,
|
||
DataHeader,
|
||
DataTotalLength,
|
||
ReceiveContext,
|
||
TRUE,
|
||
Loopback);
|
||
}
|
||
|
||
//
|
||
// Update our "bytes received" counters.
|
||
//
|
||
|
||
Link->Provider->TempIFrameBytesReceived += DataTotalLength;
|
||
++Link->Provider->TempIFramesReceived;
|
||
|
||
SkipProcessIndicateData:
|
||
|
||
break;
|
||
|
||
case NBF_CMD_DATA_ACK:
|
||
|
||
connection->IndicationInProgress = FALSE;
|
||
|
||
//
|
||
// This returns with the lock held.
|
||
//
|
||
|
||
CompleteSend(
|
||
connection,
|
||
TRANSMIT_CORR(nbfHeader));
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case NBF_CMD_SESSION_CONFIRM:
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
Status = ProcessSessionConfirm (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_SESSION_END:
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
Status = ProcessSessionEnd (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_SESSION_INITIALIZE:
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
Status = ProcessSessionInitialize (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_NO_RECEIVE:
|
||
|
||
//
|
||
// This releases the connection spinlock.
|
||
//
|
||
|
||
Status = ProcessNoReceive (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_RECEIVE_OUTSTANDING:
|
||
|
||
//
|
||
// This releases the connection spinlock.
|
||
//
|
||
|
||
Status = ProcessReceiveOutstanding (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_RECEIVE_CONTINUE:
|
||
|
||
//
|
||
// This releases the connection spinlock.
|
||
//
|
||
|
||
Status = ProcessReceiveContinue (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
case NBF_CMD_SESSION_ALIVE:
|
||
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
Status = ProcessSessionAlive (
|
||
connection,
|
||
nbfHeader);
|
||
break;
|
||
|
||
//
|
||
// An unrecognized command was found in a NetBIOS frame. Because
|
||
// this is a connection-oriented frame, we should probably shoot
|
||
// the sender, but for now we will simply discard the packet.
|
||
//
|
||
// trash the session here-- protocol violation.
|
||
//
|
||
|
||
default:
|
||
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
|
||
PANIC ("NbfProcessIIndicate: Unknown NBF command byte.\n");
|
||
connection->IndicationInProgress = FALSE;
|
||
Status = STATUS_SUCCESS;
|
||
} /* switch */
|
||
|
||
//
|
||
// A status of STATUS_MORE_PROCESSING_REQUIRED means
|
||
// that the connection reference count was inherited
|
||
// by the routine we called, so we don't do the dereference
|
||
// here.
|
||
//
|
||
|
||
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
|
||
NbfDereferenceConnectionMacro("ProcessIIndicate done", connection, CREF_PROCESS_DATA);
|
||
}
|
||
|
||
|
||
//
|
||
// The N(R) in this frame acknowleges some (or all) of our packets.
|
||
// This call must come after the checkpoint acknowlegement check
|
||
// so that an RR-r/f is always sent BEFORE any new I-frames. This
|
||
// allows us to always send I-frames as commands.
|
||
// If he responded to a checkpoint, then resend all left-over
|
||
// packets.
|
||
//
|
||
|
||
// Link->NextSend = (UCHAR)(header->RcvSeq >> 1) < Link->NextSend ?
|
||
// Link->NextSend : (UCHAR)(header->RcvSeq >> 1);
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
if (Link->WackQ.Flink != &Link->WackQ) {
|
||
|
||
UCHAR AckSequenceNumber = (UCHAR)(header->RcvSeq >> 1);
|
||
|
||
//
|
||
// Verify that the sequence number is reasonable.
|
||
//
|
||
|
||
if (Link->NextSend >= Link->LastAckReceived) {
|
||
|
||
//
|
||
// There is no 127 -> 0 wrap between the two...
|
||
//
|
||
|
||
if ((AckSequenceNumber < Link->LastAckReceived) ||
|
||
(AckSequenceNumber > Link->NextSend)) {
|
||
goto NoResend;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// There is a 127 -> 0 wrap between the two...
|
||
//
|
||
|
||
if ((AckSequenceNumber < Link->LastAckReceived) &&
|
||
(AckSequenceNumber > Link->NextSend)) {
|
||
goto NoResend;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// NOTE: ResendLlcPackets may release and
|
||
// reacquire the link spinlock.
|
||
//
|
||
|
||
(VOID)ResendLlcPackets(
|
||
Link,
|
||
AckSequenceNumber,
|
||
(BOOLEAN)((!Command) && PollFinal));
|
||
|
||
NoResend:;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Get link going again.
|
||
//
|
||
// NOTE: RestartLinkTraffic releases the link spinlock
|
||
//
|
||
|
||
RestartLinkTraffic (Link);
|
||
break;
|
||
|
||
case LINK_STATE_ADM:
|
||
|
||
//
|
||
// used to be, we'd just blow off the other guy with a DM and go home.
|
||
// it seems that OS/2 likes to believe (under some conditions) that
|
||
// it has a link up and it is still potentially active (probably
|
||
// because we return the same connection number to him that he used
|
||
// to be using). This would all be ok, except for the fact that we
|
||
// may have a connection hanging on this link waiting for a listen
|
||
// to finish. If we're in that state, go ahead and accept the
|
||
// connect.
|
||
// Set our values for link packet serial numbers to what he wants.
|
||
//
|
||
|
||
if (!IsListEmpty (&Link->ConnectionDatabase)) {
|
||
if (nbfHeader->Command == NBF_CMD_SESSION_INITIALIZE) {
|
||
|
||
//
|
||
// OK, we're at the only legal case. We've gotten an SI
|
||
// and we have a connection on this link. If the connection
|
||
// is waiting SI, we will go ahead and make believe we did
|
||
// all the correct stuff before we got it.
|
||
//
|
||
|
||
for (
|
||
p = Link->ConnectionDatabase.Flink, connection = NULL;
|
||
p != &Link->ConnectionDatabase ;
|
||
p = p->Flink, connection = NULL
|
||
) {
|
||
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if ((connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) {
|
||
// This reference is removed below
|
||
NbfReferenceConnection ("Found Listener at session init", connection, CREF_ADM_SESS);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Well, we've looked through the connections, if we have one,
|
||
// make it the connection of the day. Note that it will
|
||
// complete when we call ProcessSessionInitialize.
|
||
//
|
||
|
||
if (connection != NULL) {
|
||
|
||
Link->NextReceive = (UCHAR)(header->SendSeq >> 1) & (UCHAR)0x7f;
|
||
Link->NextSend = (UCHAR)(header->RcvSeq >> 1) & (UCHAR)0x7F;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
NbfCompleteLink (Link); // completes the listening connection
|
||
|
||
Status = ProcessSessionInitialize (
|
||
connection,
|
||
nbfHeader);
|
||
NbfDereferenceConnection ("Processed SessInit", connection, CREF_ADM_SESS);
|
||
|
||
#if DBG
|
||
s = "ADM";
|
||
#endif
|
||
|
||
// Link is ready for use.
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// we've got a connection on a link that's in state admin.
|
||
// really bad, kill it and the link.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
#if DBG
|
||
if (NbfDisconnectDebug) {
|
||
NbfPrint0( "NbfProcessIIndicate calling NbfStopLink\n" );
|
||
}
|
||
#endif
|
||
NbfStopLink (Link);
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
}
|
||
|
||
//
|
||
// We're disconnected. Tell him.
|
||
//
|
||
|
||
NbfSendDm (Link, PollFinal); // releases lock
|
||
#if DBG
|
||
s = "ADM";
|
||
#endif
|
||
break;
|
||
|
||
case LINK_STATE_CONNECTING:
|
||
|
||
//
|
||
// We've sent a SABME and are waiting for a UA. He's sent an
|
||
// I-frame too early, so just let the SABME time out.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
#if DBG
|
||
s = "CONNECTING";
|
||
#endif
|
||
break;
|
||
|
||
case LINK_STATE_W_POLL:
|
||
|
||
//
|
||
// We're waiting for his initial poll on a RR-c/p. If he starts
|
||
// with an I-frame, then we'll let him squeak by.
|
||
//
|
||
|
||
if (!Command) {
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
#if DBG
|
||
s = "W_POLL";
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
Link->State = LINK_STATE_READY; // we're up!
|
||
StopT1 (Link); // no longer waiting.
|
||
FakeUpdateBaseT1Timeout (Link);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
NbfCompleteLink (Link); // fire up the connections.
|
||
StartTi (Link);
|
||
NbfProcessIIndicate ( // recursive, but safe
|
||
Command,
|
||
PollFinal,
|
||
Link,
|
||
DlcHeader,
|
||
DlcIndicatedLength,
|
||
DlcTotalLength,
|
||
ReceiveContext,
|
||
Loopback);
|
||
#if DBG
|
||
s = "W_POLL";
|
||
#endif
|
||
break;
|
||
|
||
case LINK_STATE_W_FINAL:
|
||
|
||
//
|
||
// We're waiting for a RR-r/f from the remote guy. I-r/f will do.
|
||
//
|
||
|
||
if (Command || !PollFinal) { // don't allow this protocol.
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
#if DBG
|
||
s = "W_FINAL";
|
||
#endif
|
||
break; // we sent RR-c/p.
|
||
}
|
||
|
||
Link->State = LINK_STATE_READY; // we're up.
|
||
StopT1 (Link); // no longer waiting.
|
||
StartT2 (Link); // we have an unacked I-frame.
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
NbfCompleteLink (Link); // fire up the connections.
|
||
StartTi (Link);
|
||
NbfProcessIIndicate ( // recursive, but safe
|
||
Command,
|
||
PollFinal,
|
||
Link,
|
||
DlcHeader,
|
||
DlcIndicatedLength,
|
||
DlcTotalLength,
|
||
ReceiveContext,
|
||
Loopback);
|
||
#if DBG
|
||
s = "W_FINAL";
|
||
#endif
|
||
break;
|
||
|
||
case LINK_STATE_W_DISC_RSP:
|
||
|
||
//
|
||
// We're waiting for a response from our DISC-c/p but instead of
|
||
// a UA-r/f, we got this I-frame. Throw the packet away.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
#if DBG
|
||
s = "W_DISC_RSP";
|
||
#endif
|
||
break;
|
||
|
||
|
||
default:
|
||
|
||
ASSERT (FALSE);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
#if DBG
|
||
s = "Unknown link state";
|
||
#endif
|
||
|
||
} /* switch */
|
||
|
||
#if DBG
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint1 (" NbfProcessIIndicate: (%s) I-Frame processed.\n", s);
|
||
}
|
||
#endif
|
||
|
||
return;
|
||
} /* NbfProcessIIndicate */
|
||
|
||
|
||
NTSTATUS
|
||
ProcessIndicateData(
|
||
IN PTP_CONNECTION Connection,
|
||
IN PUCHAR DlcHeader,
|
||
IN UINT DlcIndicatedLength,
|
||
IN PUCHAR DataHeader,
|
||
IN UINT DataTotalLength,
|
||
IN NDIS_HANDLE ReceiveContext,
|
||
IN BOOLEAN Last,
|
||
IN BOOLEAN Loopback
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to process data received in a DATA_FIRST_MIDDLE
|
||
or DATA_ONLY_LAST frame. We attempt to satisfy as many TdiReceive
|
||
requests as possible with this data.
|
||
|
||
If a receive is already active on this Connection, then we copy as much
|
||
data into the active receive's buffer as possible. If all the data is
|
||
copied and the receive request's buffer has not been filled, then the
|
||
Last flag is checked, and if it is TRUE, we go ahead and complete the
|
||
current receive with the TDI_END_OF_RECORD receive indicator. If Last
|
||
is FALSE, we simply return.
|
||
|
||
If more (uncopied) data remains in the frame, or if there is no active
|
||
receive outstanding, then an indication is issued to the owning address's
|
||
receive event handler. The event handler can take one of three actions:
|
||
|
||
1. Return STATUS_SUCCESS, in which case the transport will assume that
|
||
all of the indicated data has been accepted by the client.
|
||
|
||
3. Return STATUS_DATA_NOT_ACCEPTED, in which case the transport will
|
||
discard the data and set the CONNECTION_FLAGS_RECEIVE_WAKEUP bitflag
|
||
in the Connection, indicating that remaining data is to be discarded
|
||
until a receive becomes available.
|
||
|
||
NOTE: This routine is called with Connection->LinkSpinLock held,
|
||
and returns with it released. THIS ROUTINE MUST BE CALLED AT
|
||
DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a TP_CONNECTION object.
|
||
|
||
DlcHeader - The pointer handed to us as the start of the NBF header by NDIS;
|
||
use this to compute the offset into the packet to start the transfer
|
||
of data to user buffers.
|
||
|
||
DlcIndicatedLength - The amount of NBF data available at indicate.
|
||
|
||
DataHeader - A pointer to the start of the data in the packet.
|
||
|
||
DataTotalLength - The total length of the packet, starting at DataHeader.
|
||
|
||
ReceiveContext - An NDIS handle that identifies the packet we are currently
|
||
processing.
|
||
|
||
Last - Boolean value that indicates whether this is the last piece of data
|
||
in a message. The DATA_ONLY_LAST processor sets this flag to TRUE when
|
||
calling this routine, and the DATA_FIRST_MIDDLE processor resets this
|
||
flag to FALSE when calling this routine.
|
||
|
||
Loopback - Is this a loopback indication; used to determine whether
|
||
to call NdisTransferData or NbfTransferLoopbackData.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we've consumed the packet,
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status, tmpstatus;
|
||
PDEVICE_CONTEXT deviceContext;
|
||
NDIS_STATUS ndisStatus;
|
||
PNDIS_PACKET ndisPacket;
|
||
PSINGLE_LIST_ENTRY linkage;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PNDIS_BUFFER ndisBuffer;
|
||
ULONG destBytes;
|
||
ULONG bufferChainLength;
|
||
ULONG indicateBytesTransferred;
|
||
ULONG ReceiveFlags;
|
||
ULONG ndisBytesTransferred;
|
||
UINT BytesToTransfer;
|
||
ULONG bytesIndicated;
|
||
ULONG DataOffset = (ULONG)((PUCHAR)DataHeader - (PUCHAR)DlcHeader);
|
||
PRECEIVE_PACKET_TAG receiveTag;
|
||
PTP_ADDRESS_FILE addressFile;
|
||
PMDL SavedCurrentMdl;
|
||
ULONG SavedCurrentByteOffset;
|
||
BOOLEAN ActivatedLongReceive = FALSE;
|
||
BOOLEAN CompleteReceiveBool, EndOfMessage;
|
||
ULONG DumpData[2];
|
||
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint4 (" ProcessIndicateData: Entered, PacketStart: %lx Offset: %lx \n TotalLength %ld DlcIndicatedLength: %ld\n",
|
||
DlcHeader, DataOffset, DataTotalLength, DlcIndicatedLength);
|
||
}
|
||
|
||
|
||
//
|
||
// copy this packet into our receive buffer.
|
||
//
|
||
|
||
deviceContext = Connection->Provider;
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_RCV_CANCELLED) != 0) {
|
||
|
||
//
|
||
// A receive in progress was cancelled; we toss the data,
|
||
// but do send the DOL if it was the last piece of the
|
||
// send.
|
||
//
|
||
|
||
if (Last) {
|
||
|
||
Connection->Flags &= ~CONNECTION_FLAGS_RCV_CANCELLED;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
NbfSendDataAck (Connection);
|
||
|
||
} else {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
}
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Initialize this to zero, in case we do not indicate or
|
||
// the client does not fill it in.
|
||
//
|
||
|
||
indicateBytesTransferred = 0;
|
||
|
||
if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) {
|
||
|
||
//
|
||
// check first to see if there is a receive available. If there is,
|
||
// use it before doing an indication.
|
||
//
|
||
|
||
if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n");
|
||
}
|
||
|
||
//
|
||
// Found a receive, so make it the active one and
|
||
// cycle around again.
|
||
//
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageBytesAcked = 0;
|
||
Connection->MessageInitAccepted = 0;
|
||
Connection->CurrentReceiveIrp =
|
||
CONTAINING_RECORD (Connection->ReceiveQueue.Flink,
|
||
IRP, Tail.Overlay.ListEntry);
|
||
Connection->CurrentReceiveSynchronous =
|
||
deviceContext->MacInfo.SingleReceive;
|
||
Connection->CurrentReceiveMdl =
|
||
Connection->CurrentReceiveIrp->MdlAddress;
|
||
Connection->ReceiveLength =
|
||
IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation (Connection->CurrentReceiveIrp));
|
||
Connection->ReceiveByteOffset = 0;
|
||
status = STATUS_SUCCESS;
|
||
goto NormalReceive;
|
||
}
|
||
|
||
//
|
||
// A receive is not active. Post a receive event.
|
||
//
|
||
|
||
if ((Connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) {
|
||
Connection->IndicationInProgress = FALSE;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
addressFile = Connection->AddressFile;
|
||
|
||
if ((!addressFile->RegisteredReceiveHandler) ||
|
||
(Connection->ReceiveBytesUnaccepted != 0)) {
|
||
|
||
//
|
||
// There is no receive posted to the Connection, and
|
||
// no event handler. Set the RECEIVE_WAKEUP bit, so that when a
|
||
// receive does become available, it will restart the
|
||
// current send. Also send a NoReceive to tell the other
|
||
// guy he needs to resynch.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n");
|
||
}
|
||
Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
// NbfSendNoReceive (Connection);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Receive not active. Posting event.\n");
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
LEAVE_NBF;
|
||
|
||
//
|
||
// Indicate to the user. For BytesAvailable we
|
||
// always use DataTotalLength; for BytesIndicated we use
|
||
// MIN (DlcIndicatedLength - DataOffset, DataTotalLength).
|
||
//
|
||
// To clarify BytesIndicated, on an Ethernet packet
|
||
// which is padded DataTotalLength will be shorter; on an
|
||
// Ethernet packet which is not padded and which is
|
||
// completely indicated, the two will be equal; and
|
||
// on a long Ethernet packet DlcIndicatedLength - DataOffset
|
||
// will be shorter.
|
||
//
|
||
|
||
bytesIndicated = DlcIndicatedLength - DataOffset;
|
||
if (DataTotalLength <= bytesIndicated) {
|
||
bytesIndicated = DataTotalLength;
|
||
}
|
||
|
||
ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL;
|
||
if (Last) {
|
||
ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
||
}
|
||
if (deviceContext->MacInfo.CopyLookahead) {
|
||
ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD;
|
||
}
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint2("ProcessIndicateData: Indicating - Bytes Indi =%lx, DataTotalLen =%lx.\n",
|
||
bytesIndicated, DataTotalLength);
|
||
}
|
||
|
||
status = (*addressFile->ReceiveHandler)(
|
||
addressFile->ReceiveHandlerContext,
|
||
Connection->Context,
|
||
ReceiveFlags,
|
||
bytesIndicated,
|
||
DataTotalLength, // BytesAvailable
|
||
&indicateBytesTransferred,
|
||
DataHeader,
|
||
&irp);
|
||
|
||
#if PKT_LOG
|
||
// We indicated here, log packet indicated for debugging
|
||
NbfLogIndPacket(Connection,
|
||
DataHeader,
|
||
DataTotalLength,
|
||
bytesIndicated,
|
||
indicateBytesTransferred,
|
||
status);
|
||
#endif
|
||
|
||
ENTER_NBF;
|
||
|
||
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
|
||
ULONG SpecialIrpLength;
|
||
PTDI_REQUEST_KERNEL_RECEIVE Parameters;
|
||
|
||
//
|
||
// The client's event handler has returned an IRP in the
|
||
// form of a TdiReceive that is to be associated with this
|
||
// data. The request will be installed at the front of the
|
||
// ReceiveQueue, and then made the active receive request.
|
||
// This request will be used to accept the incoming data, which
|
||
// will happen below.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Status=STATUS_MORE_PROCESSING_REQUIRED.\n");
|
||
NbfPrint4 (" ProcessIndicateData: Irp=%lx, Mdl=%lx, UserBuffer=%lx, Count=%ld.\n",
|
||
irp, irp->MdlAddress, irp->UserBuffer,
|
||
MmGetMdlByteCount (irp->MdlAddress));
|
||
}
|
||
|
||
//
|
||
// Queueing a receive of any kind causes a Connection reference;
|
||
// that's what we've just done here, so make the Connection stick
|
||
// around. We create a request to keep a packets outstanding ref
|
||
// count for the current IRP; we queue this on the connection's
|
||
// receive queue so we can treat it like a normal receive. If
|
||
// we can't get a request to describe this irp, we can't keep it
|
||
// around hoping for better later; we simple fail it with
|
||
// insufficient resources. Note this is only likely to happen if
|
||
// we've completely run out of transport memory.
|
||
//
|
||
|
||
irp->IoStatus.Information = 0; // byte transfer count.
|
||
irp->IoStatus.Status = STATUS_PENDING;
|
||
irpSp = IoGetCurrentIrpStackLocation (irp);
|
||
|
||
ASSERT (irpSp->FileObject->FsContext == Connection);
|
||
|
||
Parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
|
||
SpecialIrpLength = Parameters->ReceiveLength;
|
||
|
||
//
|
||
// If the packet is a DOL, and it will fit entirely
|
||
// inside this posted IRP, then we don't bother
|
||
// creating a request, because we don't need any of
|
||
// that overhead. We also don't set ReceiveBytes
|
||
// Unaccepted, since this receive would clear it
|
||
// anyway.
|
||
//
|
||
|
||
if (Last &&
|
||
(SpecialIrpLength >= (DataTotalLength - indicateBytesTransferred))) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
Connection->SpecialReceiveIrp = irp;
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
Connection->ReceiveLength = SpecialIrpLength;
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageInitAccepted = indicateBytesTransferred;
|
||
Connection->MessageBytesAcked = 0;
|
||
Connection->CurrentReceiveIrp = NULL;
|
||
Connection->CurrentReceiveSynchronous = TRUE;
|
||
Connection->CurrentReceiveMdl = irp->MdlAddress;
|
||
Connection->ReceiveByteOffset = 0;
|
||
if ((Parameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0) {
|
||
Connection->CurrentReceiveAckQueueable = FALSE;
|
||
}
|
||
|
||
#if DBG
|
||
//
|
||
// switch our reference from PROCESS_DATA to
|
||
// RECEIVE_IRP, this is OK because the RECEIVE_IRP
|
||
// reference won't be removed until Transfer
|
||
// DataComplete, which is the last thing
|
||
// we call.
|
||
//
|
||
|
||
NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP);
|
||
NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA);
|
||
#endif
|
||
|
||
} else {
|
||
KIRQL cancelIrql;
|
||
|
||
//
|
||
// The normal path, for longer receives.
|
||
//
|
||
|
||
IoAcquireCancelSpinLock(&cancelIrql);
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
IRP_RECEIVE_IRP(irpSp) = irp;
|
||
if (deviceContext->MacInfo.SingleReceive) {
|
||
IRP_RECEIVE_REFCOUNT(irpSp) = 1;
|
||
} else {
|
||
#if DBG
|
||
IRP_RECEIVE_REFCOUNT(irpSp) = 1;
|
||
NbfReferenceReceiveIrpLocked ("Transfer Data", irpSp, RREF_RECEIVE);
|
||
#else
|
||
IRP_RECEIVE_REFCOUNT(irpSp) = 2; // include one for first xfer
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// If the Connection is stopping, abort this request.
|
||
//
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) {
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
NbfReferenceConnection("Special IRP stopping", Connection, CREF_RECEIVE_IRP);
|
||
NbfCompleteReceiveIrp (
|
||
irp,
|
||
Connection->Status,
|
||
0);
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
IoReleaseCancelSpinLock(cancelIrql);
|
||
|
||
if (!deviceContext->MacInfo.SingleReceive) {
|
||
NbfDereferenceReceiveIrp ("Not ready", irpSp, RREF_RECEIVE);
|
||
}
|
||
return STATUS_SUCCESS; // we have consumed the packet
|
||
|
||
}
|
||
|
||
//
|
||
// If this IRP has been cancelled, complete it now.
|
||
//
|
||
|
||
if (irp->Cancel) {
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP;
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
NbfReferenceConnection("Special IRP cancelled", Connection, CREF_RECEIVE_IRP);
|
||
|
||
//
|
||
// It is safe to call this with locks held.
|
||
//
|
||
NbfCompleteReceiveIrp (irp, STATUS_CANCELLED, 0);
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
IoReleaseCancelSpinLock(cancelIrql);
|
||
|
||
if (!deviceContext->MacInfo.SingleReceive) {
|
||
NbfDereferenceReceiveIrp ("Cancelled", irpSp, RREF_RECEIVE);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Insert the request on the head of the connection's
|
||
// receive queue, so it can be handled like a normal
|
||
// receive.
|
||
//
|
||
|
||
InsertHeadList (&Connection->ReceiveQueue, &irp->Tail.Overlay.ListEntry);
|
||
|
||
IoSetCancelRoutine(irp, NbfCancelReceive);
|
||
|
||
//
|
||
// Release the cancel spinlock out of order. Since we were
|
||
// at DPC level when we acquired it, we don't have to fiddle
|
||
// with swapping irqls.
|
||
//
|
||
ASSERT(cancelIrql == DISPATCH_LEVEL);
|
||
IoReleaseCancelSpinLock(cancelIrql);
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
Connection->ReceiveLength = Parameters->ReceiveLength;
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageInitAccepted = indicateBytesTransferred;
|
||
Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred;
|
||
Connection->MessageBytesAcked = 0;
|
||
Connection->CurrentReceiveIrp = irp;
|
||
Connection->CurrentReceiveSynchronous =
|
||
deviceContext->MacInfo.SingleReceive;
|
||
Connection->CurrentReceiveMdl = irp->MdlAddress;
|
||
Connection->ReceiveByteOffset = 0;
|
||
|
||
#if DBG
|
||
//
|
||
// switch our reference from PROCESS_DATA to
|
||
// REQUEST, this is OK because the REQUEST
|
||
// reference won't be removed until Transfer
|
||
// DataComplete, which is the last thing
|
||
// we call.
|
||
//
|
||
|
||
NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP);
|
||
NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA);
|
||
#endif
|
||
//
|
||
// Make a note so we know what to do below.
|
||
//
|
||
|
||
ActivatedLongReceive = TRUE;
|
||
|
||
#if DBG
|
||
NbfReceives[NbfReceivesNext].Irp = irp;
|
||
NbfReceivesNext = (NbfReceivesNext++) % TRACK_TDI_LIMIT;
|
||
#endif
|
||
}
|
||
|
||
} else if (status == STATUS_SUCCESS) {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Status=STATUS_SUCCESS.\n");
|
||
}
|
||
|
||
//
|
||
// The client has accepted some or all of the indicated data in
|
||
// the event handler. Update MessageBytesReceived variable in
|
||
// the Connection so that if we are called upon to ACK him
|
||
// at the byte level, then we can correctly report the
|
||
// number of bytes received thus far. If this is a DOL,
|
||
// then reset the number of bytes received, since this value
|
||
// always at zero for new messages. If the data indicated wasn't
|
||
// all the data in this packet, flow control to the sender that
|
||
// didn't get all of the data.
|
||
//
|
||
|
||
if (Last && (indicateBytesTransferred >= DataTotalLength)) {
|
||
|
||
ASSERT (indicateBytesTransferred == DataTotalLength);
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
//
|
||
// This will send a DATA ACK or queue a request for
|
||
// a piggyback ack.
|
||
//
|
||
// NOTE: It will also release the connection spinlock.
|
||
//
|
||
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageInitAccepted = indicateBytesTransferred;
|
||
|
||
NbfAcknowledgeDataOnlyLast(
|
||
Connection,
|
||
Connection->MessageBytesReceived
|
||
);
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This gets gory.
|
||
// If this packet wasn't a DOL, we have no way of knowing how
|
||
// much the client will take of the data in this send that is
|
||
// now arriving. Pathological clients will break this protocol
|
||
// if they do things like taking part of the receive at indicate
|
||
// immediate and then return an irp (this would make the byte
|
||
// count wrong for the irp).
|
||
//
|
||
// Since the client did not take all the data that we
|
||
// told him about, he will eventually post a receive.
|
||
// If this has not already happened then we set the
|
||
// RECEIVE_WAKEUP bit and send a NO_RECEIVE.
|
||
//
|
||
|
||
#if DBG
|
||
NbfPrint0("NBF: Indicate returned SUCCESS but did not take all data\n");
|
||
#endif
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageInitAccepted = indicateBytesTransferred;
|
||
Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred;
|
||
Connection->MessageBytesAcked = 0;
|
||
|
||
if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) {
|
||
|
||
//
|
||
// There is no receive posted to the Connection.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n");
|
||
}
|
||
|
||
if (indicateBytesTransferred == DataTotalLength) {
|
||
|
||
//
|
||
// This means he took everything, but it was not
|
||
// a DOL; there is no need to do anything since
|
||
// the rest of the data will be right behind.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
} else {
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
NbfSendNoReceive (Connection);
|
||
|
||
}
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n");
|
||
}
|
||
|
||
//
|
||
// Found a receive, so make it the active one. This will cause
|
||
// an NdisTransferData below, so we don't dereference the
|
||
// Connection here.
|
||
//
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
Connection->CurrentReceiveIrp =
|
||
CONTAINING_RECORD (Connection->ReceiveQueue.Flink,
|
||
IRP, Tail.Overlay.ListEntry);
|
||
Connection->CurrentReceiveSynchronous =
|
||
deviceContext->MacInfo.SingleReceive;
|
||
Connection->CurrentReceiveMdl =
|
||
Connection->CurrentReceiveIrp->MdlAddress;
|
||
Connection->ReceiveLength =
|
||
IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp));
|
||
Connection->ReceiveByteOffset = 0;
|
||
}
|
||
|
||
}
|
||
|
||
} else { // STATUS_DATA_NOT_ACCEPTED or other
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Status=STATUS_DATA_NOT_ACCEPTED.\n");
|
||
}
|
||
|
||
//
|
||
// Either there is no event handler installed (the default
|
||
// handler returns this code) or the event handler is not
|
||
// able to process the received data at this time. If there
|
||
// is a TdiReceive request outstanding on this Connection's
|
||
// ReceiveQueue, then we may use it to receive this data.
|
||
// If there is no request outstanding, then we must initiate
|
||
// flow control at the transport level.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
Connection->ReceiveBytesUnaccepted = DataTotalLength;
|
||
|
||
if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) {
|
||
|
||
//
|
||
// There is no receive posted to the Connection, and
|
||
// the event handler didn't want to accept the incoming
|
||
// data. Set the RECEIVE_WAKEUP bit, so that when a
|
||
// receive does become available, it will restart the
|
||
// current send. Also send a NoReceive to tell the other
|
||
// guy he needs to resynch.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n");
|
||
}
|
||
Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP;
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n");
|
||
}
|
||
|
||
//
|
||
// Found a receive, so make it the active one. This will cause
|
||
// an NdisTransferData below, so we don't dereference the
|
||
// Connection here.
|
||
//
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageBytesAcked = 0;
|
||
Connection->MessageInitAccepted = 0;
|
||
Connection->CurrentReceiveIrp =
|
||
CONTAINING_RECORD (Connection->ReceiveQueue.Flink,
|
||
IRP, Tail.Overlay.ListEntry);
|
||
Connection->CurrentReceiveSynchronous =
|
||
deviceContext->MacInfo.SingleReceive;
|
||
Connection->CurrentReceiveMdl =
|
||
Connection->CurrentReceiveIrp->MdlAddress;
|
||
Connection->ReceiveLength =
|
||
IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp));
|
||
Connection->ReceiveByteOffset = 0;
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// A receive is active, set the status to show
|
||
// that so far.
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NormalReceive:;
|
||
|
||
//
|
||
// NOTE: The connection spinlock is held here.
|
||
//
|
||
// We should only get through here if a receive is active
|
||
// and we have not released the lock since checking or
|
||
// making one active.
|
||
//
|
||
|
||
ASSERT(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE);
|
||
|
||
IF_NBFDBG (NBF_DEBUG_RCVENG) {
|
||
NbfPrint2 (" ProcessIndicateData: Receive is active. ReceiveLengthLength: %ld Offset: %ld.\n",
|
||
Connection->ReceiveLength, Connection->MessageBytesReceived);
|
||
}
|
||
|
||
destBytes = Connection->ReceiveLength - Connection->MessageBytesReceived;
|
||
|
||
//
|
||
// If we just activated a non-special receive IRP, we already
|
||
// added a refcount for this transfer.
|
||
//
|
||
|
||
if (!Connection->CurrentReceiveSynchronous && !ActivatedLongReceive) {
|
||
NbfReferenceReceiveIrpLocked ("Transfer Data", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE);
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
|
||
//
|
||
// Determine how much data remains to be transferred.
|
||
//
|
||
|
||
ASSERT (indicateBytesTransferred <= DataTotalLength);
|
||
BytesToTransfer = DataTotalLength - indicateBytesTransferred;
|
||
|
||
if (destBytes < BytesToTransfer) {
|
||
|
||
//
|
||
// If the data overflows the current receive, then make a
|
||
// note that we should complete the receive at the end of
|
||
// transfer data, but with EOR false.
|
||
//
|
||
|
||
EndOfMessage = FALSE;
|
||
CompleteReceiveBool = TRUE;
|
||
BytesToTransfer = destBytes;
|
||
|
||
} else if (destBytes == BytesToTransfer) {
|
||
|
||
//
|
||
// If the data just fills the current receive, then complete
|
||
// the receive; EOR depends on whether this is a DOL or not.
|
||
//
|
||
|
||
EndOfMessage = Last;
|
||
CompleteReceiveBool = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Complete the receive if this is a DOL.
|
||
//
|
||
|
||
EndOfMessage = Last;
|
||
CompleteReceiveBool = Last;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If we can copy the data directly, then do so.
|
||
//
|
||
|
||
if ((BytesToTransfer > 0) &&
|
||
(DataOffset + indicateBytesTransferred + BytesToTransfer <= DlcIndicatedLength)) {
|
||
|
||
//
|
||
// All the data that we need to transfer is available in
|
||
// the indication, so copy it directly.
|
||
//
|
||
|
||
ULONG BytesNow, BytesLeft;
|
||
PUCHAR CurTarget, CurSource;
|
||
ULONG CurTargetLen;
|
||
PMDL CurMdl;
|
||
ULONG CurByteOffset;
|
||
|
||
//
|
||
// First we advance the connection pointers by the appropriate
|
||
// number of bytes, so that we can reallow indications (only
|
||
// do this if needed).
|
||
//
|
||
|
||
CurMdl = Connection->CurrentReceiveMdl;
|
||
CurByteOffset = Connection->ReceiveByteOffset;
|
||
|
||
if (!deviceContext->MacInfo.ReceiveSerialized) {
|
||
|
||
SavedCurrentMdl = CurMdl;
|
||
SavedCurrentByteOffset = CurByteOffset;
|
||
|
||
BytesLeft = BytesToTransfer;
|
||
CurTargetLen = MmGetMdlByteCount (CurMdl) - CurByteOffset;
|
||
while (TRUE) {
|
||
if (BytesLeft >= CurTargetLen) {
|
||
BytesLeft -= CurTargetLen;
|
||
CurMdl = CurMdl->Next;
|
||
CurByteOffset = 0;
|
||
if (BytesLeft == 0) {
|
||
break;
|
||
}
|
||
CurTargetLen = MmGetMdlByteCount (CurMdl);
|
||
} else {
|
||
CurByteOffset += BytesLeft;
|
||
break;
|
||
}
|
||
}
|
||
|
||
Connection->CurrentReceiveMdl = CurMdl;
|
||
Connection->ReceiveByteOffset = CurByteOffset;
|
||
Connection->MessageBytesReceived += BytesToTransfer;
|
||
|
||
//
|
||
// Set this up, we know the transfer won't
|
||
// "fail" but another one at the same time
|
||
// might.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
if (Connection->TransferBytesPending == 0) {
|
||
Connection->TransferBytesPending = BytesToTransfer;
|
||
Connection->TotalTransferBytesPending = BytesToTransfer;
|
||
Connection->SavedCurrentReceiveMdl = SavedCurrentMdl;
|
||
Connection->SavedReceiveByteOffset = SavedCurrentByteOffset;
|
||
} else {
|
||
Connection->TransferBytesPending += BytesToTransfer;
|
||
Connection->TotalTransferBytesPending += BytesToTransfer;
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
//
|
||
// Restore these for the next section of code.
|
||
//
|
||
|
||
CurMdl = SavedCurrentMdl;
|
||
CurByteOffset = SavedCurrentByteOffset;
|
||
|
||
}
|
||
|
||
CurTarget = (PUCHAR)(MmGetSystemAddressForMdl(CurMdl)) + CurByteOffset;
|
||
CurTargetLen = MmGetMdlByteCount(CurMdl) - CurByteOffset;
|
||
CurSource = DataHeader + indicateBytesTransferred;
|
||
|
||
BytesLeft = BytesToTransfer;
|
||
|
||
while (TRUE) {
|
||
|
||
if (CurTargetLen < BytesLeft) {
|
||
BytesNow = CurTargetLen;
|
||
} else {
|
||
BytesNow = BytesLeft;
|
||
}
|
||
TdiCopyLookaheadData(
|
||
CurTarget,
|
||
CurSource,
|
||
BytesNow,
|
||
deviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0);
|
||
|
||
if (BytesNow == CurTargetLen) {
|
||
BytesLeft -= BytesNow;
|
||
CurMdl = CurMdl->Next;
|
||
CurByteOffset = 0;
|
||
if (BytesLeft > 0) {
|
||
CurTarget = MmGetSystemAddressForMdl(CurMdl);
|
||
CurTargetLen = MmGetMdlByteCount(CurMdl);
|
||
CurSource += BytesNow;
|
||
} else {
|
||
break;
|
||
}
|
||
} else {
|
||
CurByteOffset += BytesNow;
|
||
ASSERT (BytesLeft == BytesNow);
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (deviceContext->MacInfo.ReceiveSerialized) {
|
||
|
||
//
|
||
// If we delayed updating these, do it now.
|
||
//
|
||
|
||
Connection->CurrentReceiveMdl = CurMdl;
|
||
Connection->ReceiveByteOffset = CurByteOffset;
|
||
Connection->MessageBytesReceived += BytesToTransfer;
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Check if something else failed and we are the
|
||
// last to complete, if so then back up our
|
||
// receive pointers and send a receive
|
||
// outstanding to make him resend.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
Connection->TransferBytesPending -= BytesToTransfer;
|
||
|
||
if ((Connection->TransferBytesPending == 0) &&
|
||
(Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) {
|
||
|
||
Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl;
|
||
Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset;
|
||
Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending;
|
||
Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL;
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
NbfSendReceiveOutstanding (Connection);
|
||
|
||
if (!Connection->SpecialReceiveIrp &&
|
||
!Connection->CurrentReceiveSynchronous) {
|
||
NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE);
|
||
}
|
||
|
||
return status;
|
||
|
||
} else {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now that the transfer is complete, simulate a call to
|
||
// TransferDataComplete.
|
||
//
|
||
|
||
|
||
if (!Connection->SpecialReceiveIrp) {
|
||
|
||
Connection->CurrentReceiveIrp->IoStatus.Information += BytesToTransfer;
|
||
if (!Connection->CurrentReceiveSynchronous) {
|
||
NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE);
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// see if we've completed the current receive. If so, move to the next one.
|
||
//
|
||
|
||
if (CompleteReceiveBool) {
|
||
CompleteReceive (Connection, EndOfMessage, BytesToTransfer);
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Get a packet for the coming transfer
|
||
//
|
||
|
||
linkage = ExInterlockedPopEntryList(
|
||
&deviceContext->ReceivePacketPool,
|
||
&deviceContext->Interlock);
|
||
|
||
if (linkage != NULL) {
|
||
ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] );
|
||
} else {
|
||
deviceContext->ReceivePacketExhausted++;
|
||
if (!Connection->CurrentReceiveSynchronous) {
|
||
NbfDereferenceReceiveIrp ("No receive packet", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE);
|
||
}
|
||
|
||
//
|
||
// We could not get a receive packet. We do have an active
|
||
// receive, so we just send a receive outstanding to
|
||
// get him to resend. Hopefully we will have a receive
|
||
// packet available when the data is resent.
|
||
//
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) {
|
||
NbfSendNoReceive (Connection);
|
||
}
|
||
NbfSendReceiveOutstanding (Connection);
|
||
|
||
#if DBG
|
||
NbfPrint0 (" ProcessIndicateData: Discarding Packet, no receive packets\n");
|
||
#endif
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Initialize the receive packet.
|
||
//
|
||
|
||
receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved);
|
||
// receiveTag->PacketType = TYPE_AT_INDICATE;
|
||
receiveTag->Connection = Connection;
|
||
receiveTag->TransferDataPended = TRUE;
|
||
|
||
receiveTag->EndOfMessage = EndOfMessage;
|
||
receiveTag->CompleteReceive = CompleteReceiveBool;
|
||
|
||
|
||
//
|
||
// if we've got zero bytes left, avoid the TransferData below and
|
||
// just deliver.
|
||
//
|
||
|
||
if (BytesToTransfer <= 0) {
|
||
Connection->IndicationInProgress = FALSE;
|
||
receiveTag->TransferDataPended = FALSE;
|
||
receiveTag->AllocatedNdisBuffer = FALSE;
|
||
receiveTag->BytesToTransfer = 0;
|
||
NbfTransferDataComplete (
|
||
deviceContext,
|
||
ndisPacket,
|
||
NDIS_STATUS_SUCCESS,
|
||
0);
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// describe the right part of the user buffer to NDIS. If we can't get
|
||
// the mdl for the packet, drop dead. Bump the request reference count
|
||
// so that we know we need to hold open receives until the NDIS transfer
|
||
// data requests complete.
|
||
//
|
||
|
||
SavedCurrentMdl = Connection->CurrentReceiveMdl;
|
||
SavedCurrentByteOffset = Connection->ReceiveByteOffset;
|
||
|
||
if ((Connection->ReceiveByteOffset == 0) &&
|
||
(CompleteReceiveBool)) {
|
||
|
||
//
|
||
// If we are transferring into the beginning of
|
||
// the current MDL, and we will be completing the
|
||
// receive after the transfer, then we don't need to
|
||
// copy it.
|
||
//
|
||
|
||
ndisBuffer = (PNDIS_BUFFER)Connection->CurrentReceiveMdl;
|
||
bufferChainLength = BytesToTransfer;
|
||
Connection->CurrentReceiveMdl = NULL;
|
||
// Connection->ReceiveByteOffset = 0;
|
||
receiveTag->AllocatedNdisBuffer = FALSE;
|
||
tmpstatus = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
tmpstatus = BuildBufferChainFromMdlChain (
|
||
deviceContext,
|
||
Connection->CurrentReceiveMdl,
|
||
Connection->ReceiveByteOffset,
|
||
BytesToTransfer,
|
||
&ndisBuffer,
|
||
&Connection->CurrentReceiveMdl,
|
||
&Connection->ReceiveByteOffset,
|
||
&bufferChainLength);
|
||
|
||
receiveTag->AllocatedNdisBuffer = TRUE;
|
||
|
||
}
|
||
|
||
|
||
if ((!NT_SUCCESS (tmpstatus)) || (bufferChainLength != BytesToTransfer)) {
|
||
|
||
DumpData[0] = bufferChainLength;
|
||
DumpData[1] = BytesToTransfer;
|
||
|
||
NbfWriteGeneralErrorLog(
|
||
deviceContext,
|
||
EVENT_TRANSPORT_TRANSFER_DATA,
|
||
604,
|
||
tmpstatus,
|
||
NULL,
|
||
2,
|
||
DumpData);
|
||
|
||
if (!Connection->CurrentReceiveSynchronous) {
|
||
NbfDereferenceReceiveIrp ("No MDL chain", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE);
|
||
}
|
||
|
||
//
|
||
// Restore our old state and make him resend.
|
||
//
|
||
|
||
Connection->CurrentReceiveMdl = SavedCurrentMdl;
|
||
Connection->ReceiveByteOffset = SavedCurrentByteOffset;
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) {
|
||
NbfSendNoReceive (Connection);
|
||
}
|
||
NbfSendReceiveOutstanding (Connection);
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
ExInterlockedPushEntryList(
|
||
&deviceContext->ReceivePacketPool,
|
||
&receiveTag->Linkage,
|
||
&deviceContext->Interlock);
|
||
|
||
return status;
|
||
}
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint3 (" ProcessIndicateData: Mdl: %lx user buffer: %lx user offset: %lx \n",
|
||
ndisBuffer, Connection->CurrentReceiveMdl, Connection->ReceiveByteOffset);
|
||
}
|
||
|
||
NdisChainBufferAtFront (ndisPacket, ndisBuffer);
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint1 (" ProcessIndicateData: Transferring Complete Packet: %lx\n",
|
||
ndisPacket);
|
||
}
|
||
|
||
//
|
||
// update the number of bytes received; OK to do this
|
||
// unprotected since IndicationInProgress is still FALSE.
|
||
//
|
||
//
|
||
|
||
Connection->MessageBytesReceived += BytesToTransfer;
|
||
|
||
//
|
||
// We have to do this for two reasons: for MACs that
|
||
// are not receive-serialized, to keep track of it,
|
||
// and for MACs where transfer data can pend, so
|
||
// we have stuff saved to handle failure later (if
|
||
// the MAC is synchronous on transfers and it fails,
|
||
// we fill these fields in before calling CompleteTransferData).
|
||
//
|
||
|
||
if (!deviceContext->MacInfo.SingleReceive) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
receiveTag->BytesToTransfer = BytesToTransfer;
|
||
if (Connection->TransferBytesPending == 0) {
|
||
Connection->TransferBytesPending = BytesToTransfer;
|
||
Connection->TotalTransferBytesPending = BytesToTransfer;
|
||
Connection->SavedCurrentReceiveMdl = SavedCurrentMdl;
|
||
Connection->SavedReceiveByteOffset = SavedCurrentByteOffset;
|
||
} else {
|
||
Connection->TransferBytesPending += BytesToTransfer;
|
||
Connection->TotalTransferBytesPending += BytesToTransfer;
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
}
|
||
|
||
//
|
||
// We have now updated all the connection counters (
|
||
// assuming the TransferData will succeed) and this
|
||
// packet's location in the request is secured, so we
|
||
// can be reentered.
|
||
//
|
||
|
||
Connection->IndicationInProgress = FALSE;
|
||
|
||
if (Loopback) {
|
||
|
||
NbfTransferLoopbackData(
|
||
&ndisStatus,
|
||
deviceContext,
|
||
ReceiveContext,
|
||
deviceContext->MacInfo.TransferDataOffset +
|
||
DataOffset + indicateBytesTransferred,
|
||
BytesToTransfer,
|
||
ndisPacket,
|
||
(PUINT)&ndisBytesTransferred
|
||
);
|
||
|
||
} else {
|
||
|
||
if (deviceContext->NdisBindingHandle) {
|
||
|
||
NdisTransferData (
|
||
&ndisStatus,
|
||
deviceContext->NdisBindingHandle,
|
||
ReceiveContext,
|
||
deviceContext->MacInfo.TransferDataOffset +
|
||
DataOffset + indicateBytesTransferred,
|
||
BytesToTransfer,
|
||
ndisPacket,
|
||
(PUINT)&ndisBytesTransferred);
|
||
}
|
||
else {
|
||
ndisStatus = STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// handle the various completion codes
|
||
//
|
||
|
||
if ((ndisStatus == NDIS_STATUS_SUCCESS) &&
|
||
(ndisBytesTransferred == BytesToTransfer)) {
|
||
|
||
//
|
||
// deallocate the buffers and such that we've used if at indicate
|
||
//
|
||
|
||
receiveTag->TransferDataPended = FALSE;
|
||
|
||
NbfTransferDataComplete (
|
||
deviceContext,
|
||
ndisPacket,
|
||
ndisStatus,
|
||
BytesToTransfer);
|
||
|
||
} else if (ndisStatus == NDIS_STATUS_PENDING) {
|
||
|
||
//
|
||
// Because TransferDataPended stays TRUE, this reference will
|
||
// be removed in TransferDataComplete. It is OK to do this
|
||
// now, even though TransferDataComplete may already have been
|
||
// called, because we also hold the ProcessIIndicate reference
|
||
// so there will be no "bounce".
|
||
//
|
||
|
||
NbfReferenceConnection ("TransferData pended", Connection, CREF_TRANSFER_DATA);
|
||
|
||
} else {
|
||
|
||
//
|
||
// something broke; certainly we'll never get NdisTransferData
|
||
// asynch completion with this error status. We set things up
|
||
// to that NbfTransferDataComplete will do the right thing.
|
||
//
|
||
|
||
if (deviceContext->MacInfo.SingleReceive) {
|
||
Connection->TransferBytesPending = BytesToTransfer;
|
||
Connection->TotalTransferBytesPending = BytesToTransfer;
|
||
Connection->SavedCurrentReceiveMdl = SavedCurrentMdl;
|
||
Connection->SavedReceiveByteOffset = SavedCurrentByteOffset;
|
||
receiveTag->BytesToTransfer = BytesToTransfer;
|
||
}
|
||
|
||
receiveTag->TransferDataPended = FALSE;
|
||
|
||
NbfTransferDataComplete (
|
||
deviceContext,
|
||
ndisPacket,
|
||
ndisStatus,
|
||
BytesToTransfer);
|
||
|
||
}
|
||
|
||
return status; // which only means we've dealt with the packet
|
||
|
||
} /* ProcessIndicateData */
|
||
|