You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3380 lines
112 KiB
3380 lines
112 KiB
/*++
|
|
|
|
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 */
|
|
|