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.
3676 lines
114 KiB
3676 lines
114 KiB
/*++
|
|
|
|
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sendeng.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code that implements the send engine for the
|
|
Jetbeui transport provider. This code is responsible for the following
|
|
basic activities, including some subordinate glue.
|
|
|
|
1. Packetizing TdiSend requests already queued up on a TP_CONNECTION
|
|
object, using I-frame packets acquired from the PACKET.C module,
|
|
and turning them into shippable packets and placing them on the
|
|
TP_LINK's WackQ. In the process of doing this, the packets are
|
|
actually submitted as I/O requests to the Physical Provider, in
|
|
the form of PdiSend requests.
|
|
|
|
2. Retiring packets queued to a TP_LINK's WackQ and returning them to
|
|
the device context's pool for use by other links. In the process
|
|
of retiring acked packets, step 1 may be reactivated.
|
|
|
|
3. Resending packets queued to a TP_LINK's WackQ because of a reject
|
|
condition on the link. This involves no state update in the
|
|
TP_CONNECTION object.
|
|
|
|
4. Handling of Send completion events from the Physical Provider,
|
|
to allow proper synchronization of the reuse of packets.
|
|
|
|
5. Completion of TdiSend requests. This is triggered by the receipt
|
|
(in IFRAMES.C) of a DataAck frame, or by a combination of other
|
|
frames when the proper protocol has been negotiated. One routine
|
|
in this routine is responsible for the actual mechanics of TdiSend
|
|
request completion.
|
|
|
|
Author:
|
|
|
|
David Beaver (dbeaver) 1-July-1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#if DBG
|
|
extern ULONG NbfSendsIssued;
|
|
extern ULONG NbfSendsCompletedInline;
|
|
extern ULONG NbfSendsCompletedOk;
|
|
extern ULONG NbfSendsCompletedFail;
|
|
extern ULONG NbfSendsPended;
|
|
extern ULONG NbfSendsCompletedAfterPendOk;
|
|
extern ULONG NbfSendsCompletedAfterPendFail;
|
|
#endif
|
|
|
|
|
|
//
|
|
// Temporary variables to control piggyback ack usage.
|
|
//
|
|
#define NbfUsePiggybackAcks 1
|
|
#if DBG
|
|
ULONG NbfDebugPiggybackAcks = 0;
|
|
#endif
|
|
|
|
|
|
#if DBG
|
|
//
|
|
// *** This is the original version of StartPacketizingConnection, which
|
|
// is now a macro on the free build. It has been left here as the
|
|
// fully-commented version of the code.
|
|
//
|
|
|
|
VOID
|
|
StartPacketizingConnection(
|
|
PTP_CONNECTION Connection,
|
|
IN BOOLEAN Immediate
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to place a connection on the PacketizeQueue
|
|
of its device context object. Then this routine starts packetizing
|
|
the first connection on that queue.
|
|
|
|
*** The Connection LinkSpinLock must be held on entry to this routine.
|
|
|
|
*** THIS FUNCTION MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Immediate - TRUE if the connection should be packetized
|
|
immediately; FALSE if the connection should be queued
|
|
up for later packetizing (implies that ReceiveComplete
|
|
will be called in the future, which packetizes always).
|
|
|
|
NOTE: If this is TRUE, it also implies that we have
|
|
a connection reference of type CREF_BY_ID which we
|
|
will "convert" into the CREF_PACKETIZE_QUEUE one.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("StartPacketizingConnection: Entered for connection %lx.\n",
|
|
Connection);
|
|
}
|
|
|
|
DeviceContext = Connection->Provider;
|
|
|
|
//
|
|
// If this connection's SendState is set to PACKETIZE and if
|
|
// we are not already on the PacketizeQueue, then go ahead and
|
|
// append us to the end of that queue, and remember that we're
|
|
// on it by setting the CONNECTION_FLAGS_PACKETIZE bitflag.
|
|
//
|
|
// Also don't queue it if the connection is stopping.
|
|
//
|
|
|
|
if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) &&
|
|
!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE) &&
|
|
(Connection->Flags & CONNECTION_FLAGS_READY)) {
|
|
|
|
ASSERT (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING));
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
if (!Immediate) {
|
|
NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE);
|
|
} else {
|
|
#if DBG
|
|
NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE);
|
|
NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID);
|
|
#endif
|
|
}
|
|
|
|
ExInterlockedInsertTailList(
|
|
&DeviceContext->PacketizeQueue,
|
|
&Connection->PacketizeLinkage,
|
|
&DeviceContext->SpinLock);
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
} else {
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
if (Immediate) {
|
|
NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID);
|
|
}
|
|
}
|
|
|
|
if (Immediate) {
|
|
PacketizeConnections (DeviceContext);
|
|
}
|
|
|
|
} /* StartPacketizingConnection */
|
|
#endif
|
|
|
|
|
|
VOID
|
|
PacketizeConnections(
|
|
PDEVICE_CONTEXT DeviceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to packetize all connections waiting on the
|
|
PacketizeQueue of the DeviceContext.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to a DEVICE_CONTEXT object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY p;
|
|
PTP_CONNECTION Connection;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("PacketizeConnections: Entered for device context %lx.\n",
|
|
DeviceContext);
|
|
}
|
|
|
|
//
|
|
// Pick connections off of the device context's packetization queue
|
|
// until there are no more left to pick off. For each one, we call
|
|
// PacketizeSend. Note this routine can be executed concurrently
|
|
// on multiple processors and it doesn't matter; multiple connections
|
|
// may be packetized concurrently.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
p = ExInterlockedRemoveHeadList(
|
|
&DeviceContext->PacketizeQueue,
|
|
&DeviceContext->SpinLock);
|
|
|
|
if (p == NULL) {
|
|
break;
|
|
}
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) {
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE);
|
|
} else {
|
|
NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET);
|
|
PacketizeSend (Connection, FALSE); // releases the lock.
|
|
}
|
|
}
|
|
|
|
} /* PacketizeConnections */
|
|
|
|
|
|
VOID
|
|
PacketizeSend(
|
|
IN PTP_CONNECTION Connection,
|
|
IN BOOLEAN Direct
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packetizes the current TdiSend request on the specified
|
|
connection as much as limits will permit. A given here is that there
|
|
is an active send on the connection that needs further packetization.
|
|
|
|
NOTE: This routine is called with the connection spinlock held and
|
|
returns with it released. THIS FUNCTION MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Direct - TRUE if we are called from TdiSend. This implies that
|
|
the connection does not have a reference of type CREF_SEND_IRP,
|
|
which we need to add before we leave.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG MaxFrameSize, FrameSize;
|
|
ULONG PacketBytes;
|
|
PNDIS_BUFFER PacketDescriptor;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PTP_PACKET Packet;
|
|
NTSTATUS Status;
|
|
PNBF_HDR_CONNECTION NbfHeader;
|
|
BOOLEAN LinkCheckpoint;
|
|
BOOLEAN SentPacket = FALSE;
|
|
BOOLEAN ExitAfterSendOnePacket = FALSE;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
ULONG LastPacketLength;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("PacketizeSend: Entered for connection %lx.\n", Connection);
|
|
}
|
|
|
|
DeviceContext = Connection->Provider;
|
|
|
|
ASSERT (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE);
|
|
|
|
//
|
|
// Just loop until one of three events happens: (1) we run out of
|
|
// packets from NbfCreatePacket, (2) we completely packetize the send,
|
|
// or (3) we can't send any more packets because SendOnePacket failed.
|
|
//
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Convert the queue reference into a packetize one. It is OK
|
|
// to do this with the lock held because we know that the refcount
|
|
// must already be at least one, so we don't drop to zero.
|
|
//
|
|
|
|
NbfReferenceConnection ("PacketizeSend", Connection, CREF_PACKETIZE);
|
|
NbfDereferenceConnection ("Off packetize queue", Connection, CREF_PACKETIZE_QUEUE);
|
|
#endif
|
|
|
|
MaxFrameSize = Connection->MaximumDataSize;
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("PacketizeSend: MaxFrameSize for user data=%ld.\n", MaxFrameSize);
|
|
}
|
|
|
|
|
|
//
|
|
// It is possible for a frame to arrive during the middle of this loop
|
|
// (such as a NO_RECEIVE) that will put us into a new state (such as
|
|
// W_RCVCONT). For this reason, we have to check the state every time
|
|
// (at the end of the loop).
|
|
//
|
|
|
|
do {
|
|
|
|
if (!NT_SUCCESS (NbfCreatePacket (DeviceContext, Connection->Link, &Packet))) {
|
|
|
|
//
|
|
// We need a packet to finish packetizing the current send, but
|
|
// there are no more packets available in the pool right now.
|
|
// Set our send state to W_PACKET, and put this connection on
|
|
// the PacketWaitQueue of the device context object. Then,
|
|
// when NbfDestroyPacket frees up a packet, it will check this
|
|
// queue for starved connections, and if it finds one, it will
|
|
// take a connection off the list and set its send state to
|
|
// SENDSTATE_PACKETIZE and put it on the PacketizeQueue.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint0 ("PacketizeSend: NbfCreatePacket failed.\n");
|
|
}
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_PACKET;
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer
|
|
// on the PacketizeQueue or actively packetizing. The flag
|
|
// was set by StartPacketizingConnection to indicate that
|
|
// the connection was already on the PacketizeQueue.
|
|
//
|
|
// Don't queue him if the connection is stopping.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
#if DBG
|
|
if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) {
|
|
DbgPrint ("NBF: Trying to PacketWait stopping connection %lx\n", Connection);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
Connection->Flags |= CONNECTION_FLAGS_W_PACKETIZE;
|
|
if (!Connection->OnPacketWaitQueue) {
|
|
Connection->OnPacketWaitQueue = TRUE;
|
|
InsertTailList(
|
|
&DeviceContext->PacketWaitQueue,
|
|
&Connection->PacketWaitLinkage);
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
if (!SentPacket) {
|
|
NbfDereferenceSendIrp ("No packet", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET);
|
|
}
|
|
if (Direct) {
|
|
NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP);
|
|
}
|
|
|
|
NbfDereferenceConnection ("No packet", Connection, CREF_PACKETIZE);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the length of the packet now, while only the
|
|
// header is attached.
|
|
//
|
|
|
|
NbfSetNdisPacketLength(
|
|
Packet->NdisPacket,
|
|
Connection->Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION));
|
|
|
|
// Add a reference count to the request, and keep track of
|
|
// which request it is. We rely on NbfDestroyPacket to
|
|
// remove the reference.
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp);
|
|
|
|
Packet->Owner = IrpSp;
|
|
// Packet->Action = PACKET_ACTION_IRP_SP;
|
|
IF_NBFDBG (NBF_DEBUG_REQUEST) {
|
|
NbfPrint2 ("PacketizeSend: Packet %x ref IrpSp %x.\n", Packet, Packet->Owner);
|
|
}
|
|
|
|
//
|
|
// For performance reasons, the first time through here on
|
|
// a direct call, we have a IrpSp reference already.
|
|
//
|
|
|
|
if (SentPacket) {
|
|
NbfReferenceSendIrp ("Packetize", IrpSp, RREF_PACKET);
|
|
}
|
|
|
|
//
|
|
// Now build a DATA_ONLY_LAST header in this frame. If it
|
|
// turns out we need a DFM, we change it. The header we copy
|
|
// already has ResponseCorrelator set to our current correlator
|
|
// and TransmitCorrelator set to the last one we received from
|
|
// him (if we do not piggyback an ack, then we zero out
|
|
// TransmitCorrelator).
|
|
//
|
|
|
|
NbfHeader = (PNBF_HDR_CONNECTION)&(Packet->Header[Connection->Link->HeaderLength + sizeof(DLC_I_FRAME)]);
|
|
*(NBF_HDR_CONNECTION UNALIGNED *)NbfHeader = Connection->NetbiosHeader;
|
|
|
|
ASSERT (RESPONSE_CORR(NbfHeader) != 0);
|
|
|
|
//
|
|
// Determine if we need the resynch bit here.
|
|
//
|
|
|
|
if (Connection->Flags & CONNECTION_FLAGS_RESYNCHING) {
|
|
|
|
NbfHeader->Data2Low = 1;
|
|
Connection->Flags &= ~CONNECTION_FLAGS_RESYNCHING;
|
|
|
|
} else {
|
|
|
|
NbfHeader->Data2Low = 0;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// build an NDIS_BUFFER chain that describes the buffer we're using, and
|
|
// thread it off the NdisBuffer. This chain may not complete the
|
|
// packet, as the remaining part of the MDL chain may be shorter than
|
|
// the packet.
|
|
//
|
|
|
|
FrameSize = MaxFrameSize;
|
|
|
|
//
|
|
// Check if we have less than FrameSize left to send.
|
|
//
|
|
|
|
if (Connection->sp.MessageBytesSent + FrameSize > Connection->CurrentSendLength) {
|
|
|
|
FrameSize = Connection->CurrentSendLength - Connection->sp.MessageBytesSent;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Make a copy of the MDL chain for this send, unless
|
|
// there are zero bytes left.
|
|
//
|
|
|
|
if (FrameSize != 0) {
|
|
|
|
//
|
|
// If the whole send will fit inside one packet,
|
|
// then there is no need to duplicate the MDL
|
|
// (note that this may include multi-MDL sends).
|
|
//
|
|
|
|
if ((Connection->sp.SendByteOffset == 0) &&
|
|
(Connection->CurrentSendLength == FrameSize)) {
|
|
|
|
PacketDescriptor = (PNDIS_BUFFER)Connection->sp.CurrentSendMdl;
|
|
PacketBytes = FrameSize;
|
|
Connection->sp.CurrentSendMdl = NULL;
|
|
Connection->sp.SendByteOffset = FrameSize;
|
|
Packet->PacketNoNdisBuffer = TRUE;
|
|
|
|
} else {
|
|
|
|
Status = BuildBufferChainFromMdlChain (
|
|
DeviceContext,
|
|
Connection->sp.CurrentSendMdl,
|
|
Connection->sp.SendByteOffset,
|
|
FrameSize,
|
|
&PacketDescriptor,
|
|
&Connection->sp.CurrentSendMdl,
|
|
&Connection->sp.SendByteOffset,
|
|
&PacketBytes);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (NbfHeader->Data2Low) {
|
|
Connection->Flags |= CONNECTION_FLAGS_RESYNCHING;
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfDereferencePacket (Packet); // remove creation hold.
|
|
goto BufferChainFailure;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Chain the buffers to the packet, unless there
|
|
// are zero bytes of data.
|
|
//
|
|
|
|
Connection->sp.MessageBytesSent += PacketBytes;
|
|
NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor);
|
|
|
|
} else {
|
|
|
|
PacketBytes = 0;
|
|
Connection->sp.CurrentSendMdl = NULL;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
{PNDIS_BUFFER NdisBuffer;
|
|
NdisQueryPacket(Packet->NdisPacket, NULL, NULL, &NdisBuffer, NULL);
|
|
NbfPrint1 ("PacketizeSend: NDIS_BUFFER Built, chain is: %lx is Packet->Head\n", NdisBuffer);
|
|
NdisGetNextBuffer (NdisBuffer, &NdisBuffer);
|
|
while (NdisBuffer != NULL) {
|
|
NbfPrint1 (" %lx is Next\n",
|
|
NdisBuffer);
|
|
NdisGetNextBuffer (NdisBuffer, &NdisBuffer);
|
|
}}
|
|
}
|
|
|
|
//
|
|
// Have we run out of Mdl Chain in this request?
|
|
//
|
|
|
|
#if DBG
|
|
if (PacketBytes < FrameSize) {
|
|
ASSERT (Connection->sp.CurrentSendMdl == NULL);
|
|
}
|
|
#endif
|
|
|
|
if ((Connection->sp.CurrentSendMdl == NULL) ||
|
|
(Connection->CurrentSendLength <= Connection->sp.MessageBytesSent)) {
|
|
|
|
//
|
|
// Yep. We know that we've exhausted the current request's buffer
|
|
// here, so see if there's another request without EOF set that we
|
|
// can build start throwing into this packet.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint0 ("PacketizeSend: Used up entire request.\n");
|
|
}
|
|
|
|
if (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) {
|
|
|
|
//
|
|
// We are sending the last packet in a message. Change
|
|
// the packet type and indicate in the connection object's
|
|
// send state that we are waiting for a DATA_ACK NetBIOS-
|
|
// level acknowlegement.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint0 ("PacketizeSend: Request has EOR, making pkt a DOL.\n");
|
|
}
|
|
|
|
//
|
|
// Keep track of how many consecutive sends we have done.
|
|
//
|
|
|
|
Connection->ConsecutiveSends++;
|
|
Connection->ConsecutiveReceives = 0;
|
|
|
|
//
|
|
// Change it to a DOL with piggyback ack allowed if wanted.
|
|
//
|
|
|
|
ASSERT (NbfHeader->Command == NBF_CMD_DATA_ONLY_LAST);
|
|
if (!(IRP_SEND_FLAGS(IrpSp) &
|
|
TDI_SEND_NO_RESPONSE_EXPECTED) &&
|
|
(Connection->ConsecutiveSends < 2)) {
|
|
if (NbfUsePiggybackAcks) {
|
|
NbfHeader->Data1 |= DOL_OPTIONS_ACK_W_DATA_ALLOWED;
|
|
}
|
|
}
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_ACK;
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
ExitAfterSendOnePacket = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We are sending the last packet in this request. If there
|
|
// are more requests in the connection's SendQueue, then
|
|
// advance complex send pointer to point to the next one
|
|
// in line. Otherwise, if there aren't any more requests
|
|
// ready to packetize, then we enter the W_EOR state and
|
|
// stop packetizing. Note that we're waiting here for the TDI
|
|
// client to come up with data to send; we're just hanging out
|
|
// until then.
|
|
//
|
|
// DGB: Note that this will allow the last packet in the
|
|
// request to be smaller than the max packet length. This
|
|
// is not addressed anywhere that I can find in the NBF
|
|
// spec, and will be interesting to test against a non-NT
|
|
// NBF protocol.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint0 ("PacketizeSend: Request doesn't have EOR.\n");
|
|
}
|
|
|
|
NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE;
|
|
|
|
if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) {
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_EOR;
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
ExitAfterSendOnePacket = TRUE;
|
|
|
|
} else {
|
|
|
|
Connection->sp.CurrentSendIrp =
|
|
CONTAINING_RECORD (
|
|
Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
Connection->sp.CurrentSendMdl =
|
|
Connection->sp.CurrentSendIrp->MdlAddress;
|
|
Connection->sp.SendByteOffset = 0;
|
|
Connection->CurrentSendLength +=
|
|
IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE;
|
|
|
|
}
|
|
|
|
//
|
|
// Before we release the spinlock, see if we want to
|
|
// piggyback an ack on here.
|
|
//
|
|
|
|
if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) {
|
|
|
|
//
|
|
// Turn off the flags. We don't take it off the queue,
|
|
// that will be handled by the timer function.
|
|
//
|
|
|
|
Connection->DeferredFlags &=
|
|
~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q);
|
|
|
|
ASSERT (DOL_OPTIONS_ACK_INCLUDED == DFM_OPTIONS_ACK_INCLUDED);
|
|
|
|
#if DBG
|
|
if (NbfDebugPiggybackAcks) {
|
|
NbfPrint0("A");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// TRANSMIT_CORR(NbfHeader) is already set correctly.
|
|
//
|
|
|
|
NbfHeader->Data1 |= DOL_OPTIONS_ACK_INCLUDED;
|
|
|
|
} else {
|
|
|
|
TRANSMIT_CORR(NbfHeader) = (USHORT)0;
|
|
|
|
}
|
|
|
|
//
|
|
// To prevent a send "crossing" the receive and
|
|
// causing a bogus piggyback ack timeout (this
|
|
// only matters if a receive indication is in
|
|
// progress).
|
|
//
|
|
|
|
Connection->CurrentReceiveAckQueueable = FALSE;
|
|
|
|
SentPacket = TRUE;
|
|
LastPacketLength =
|
|
sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION) + PacketBytes;
|
|
|
|
MacModifyHeader(
|
|
&DeviceContext->MacInfo,
|
|
Packet->Header,
|
|
LastPacketLength);
|
|
|
|
Packet->NdisIFrameLength = LastPacketLength;
|
|
|
|
ASSERT (Connection->LinkSpinLock == &Connection->Link->SpinLock);
|
|
|
|
Status = SendOnePacket (Connection, Packet, FALSE, &LinkCheckpoint);
|
|
|
|
if (Status == STATUS_LINK_FAILED) {
|
|
|
|
//
|
|
// If SendOnePacket failed due to the link being
|
|
// dead, then we tear down the link.
|
|
//
|
|
|
|
FailSend (Connection, STATUS_LINK_FAILED, TRUE); // fail the send
|
|
NbfDereferencePacket (Packet); // remove creation hold.
|
|
if (Direct) {
|
|
NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP);
|
|
}
|
|
NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
//
|
|
// SendOnePacket returned success, so update our counters;
|
|
//
|
|
|
|
DeviceContext->TempIFrameBytesSent += PacketBytes;
|
|
++DeviceContext->TempIFramesSent;
|
|
|
|
if ((Status == STATUS_SUCCESS) && LinkCheckpoint) {
|
|
|
|
//
|
|
// We are checkpointing; this means that SendOnePacket
|
|
// will already have set the state to W_LINK and turned
|
|
// off the PACKETIZE flag, so we should leave. When
|
|
// the checkpoint response is received, we will
|
|
// resume packetizing. We don't have to worry about
|
|
// doing all the other recovery stuff (resetting
|
|
// the piggyback ack flag, complex send pointer, etc.)
|
|
// because the send did in fact succeed.
|
|
//
|
|
|
|
if (Direct) {
|
|
#if DBG
|
|
NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP);
|
|
NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE);
|
|
#endif
|
|
} else {
|
|
NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE);
|
|
}
|
|
return;
|
|
|
|
} else if (ExitAfterSendOnePacket ||
|
|
(Status == STATUS_MORE_PROCESSING_REQUIRED)) {
|
|
|
|
if (Direct) {
|
|
#if DBG
|
|
NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP);
|
|
NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE);
|
|
#endif
|
|
} else {
|
|
NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE);
|
|
}
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
BufferChainFailure:;
|
|
|
|
//
|
|
// Note that we may have fallen out of the BuildBuffer... if above with
|
|
// Status set to STATUS_INSUFFICIENT_RESOURCES. if we have, we'll just
|
|
// stick this connection back onto the packetize queue and hope the
|
|
// system gets more resources later.
|
|
//
|
|
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint0 ("PacketizeSend: SendOnePacket failed.\n");
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// Indicate we're waiting on favorable link conditions.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_LINK;
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer
|
|
// on the PacketizeQueue or actively packetizing. The flag
|
|
// was set by StartPacketizingConnection to indicate that
|
|
// the connection was already on the PacketizeQueue.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// If we are exiting and we sent a packet without
|
|
// polling, we need to start T1.
|
|
//
|
|
|
|
if (Direct) {
|
|
|
|
//
|
|
// We have to do the CREF_SEND_IRP reference that is missing.
|
|
//
|
|
|
|
#if DBG
|
|
NbfReferenceConnection("TdiSend", Connection, CREF_SEND_IRP);
|
|
NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE);
|
|
#endif
|
|
} else {
|
|
NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// It is probable that a NetBIOS frame arrived while we released
|
|
// the connection's spin lock, so our state has probably changed.
|
|
// When we cycle around this loop again, we will have the lock
|
|
// again, so we can test the connection's send state.
|
|
//
|
|
|
|
} while (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE);
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer on the
|
|
// PacketizeQueue or actively packetizing. The flag was set by
|
|
// StartPacketizingConnection to indicate that the connection was
|
|
// already on the PacketizeQueue.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
|
|
if (Direct) {
|
|
#if DBG
|
|
NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP);
|
|
NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE);
|
|
#endif
|
|
} else {
|
|
NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE);
|
|
}
|
|
|
|
} /* PacketizeSend */
|
|
|
|
|
|
VOID
|
|
CompleteSend(
|
|
PTP_CONNECTION Connection,
|
|
IN USHORT Correlator
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called because the connection partner acknowleged
|
|
an entire message at the NetBIOS Frames Protocol level, either through
|
|
a DATA_ACK response, or a RECEIVE_OUTSTANDING, or RECEIVE_CONTINUE,
|
|
or NO_RECEIVE response where the number of bytes specified exactly
|
|
matches the number of bytes sent in the message. Here we retire all
|
|
of the TdiSends on the connection's SendQueue up to and including the
|
|
one with the TDI_END_OF_RECORD bitflag set. For each request, we
|
|
complete the I/O.
|
|
|
|
NOTE: This function is called with the connection spinlock
|
|
held and returns with it held, but it may release it in the
|
|
middle. THIS FUNCTION MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Correlator - The correlator in the DATA_ACK or piggybacked ack.
|
|
|
|
OldIrqlP - Returns the IRQL at which the connection spinlock
|
|
was acquired.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN EndOfRecord;
|
|
KIRQL cancelIrql;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("CompleteSend: Entered for connection %lx.\n", Connection);
|
|
}
|
|
|
|
|
|
//
|
|
// Make sure that the correlator is the expect one, and
|
|
// that we are in a good state (don't worry about locking
|
|
// since this is an unusual case anyway).
|
|
//
|
|
|
|
if (Correlator != Connection->NetbiosHeader.ResponseCorrelator) {
|
|
NbfPrint0 ("NbfCompleteSend: ack ignored, wrong correlator\n");
|
|
return;
|
|
}
|
|
|
|
if (Connection->SendState != CONNECTION_SENDSTATE_W_ACK) {
|
|
NbfPrint0 ("NbfCompleteSend: ack not expected\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Pick off TP_REQUEST objects from the connection's SendQueue until
|
|
// we find one with an END_OF_RECORD mark embedded in it.
|
|
//
|
|
|
|
while (!(IsListEmpty(&Connection->SendQueue))) {
|
|
|
|
//
|
|
// We know for a fact that we wouldn't be calling this routine if
|
|
// we hadn't received an acknowlegement for an entire message,
|
|
// since NBF doesn't provide stream mode sends. Therefore, we
|
|
// know that we will run into a request with the END_OF_RECORD
|
|
// mark set BEFORE we will run out of requests on that queue,
|
|
// so there is no reason to check to see if we ran off the end.
|
|
// Note that it's possible that the send has been failed and the
|
|
// connection not yet torn down; if this has happened, we could be
|
|
// removing from an empty queue here. Make sure that doesn't happen.
|
|
//
|
|
|
|
p = RemoveHeadList(&Connection->SendQueue);
|
|
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL);
|
|
|
|
#if DBG
|
|
NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp;
|
|
NbfCompletedSends[NbfCompletedSendsNext].Request = NULL;
|
|
NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_SUCCESS;
|
|
NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT;
|
|
#endif
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TRACKTDI) {
|
|
if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_SENDS) != 0){
|
|
NbfPrint1 ("CompleteSend: Completing send request %lx\n", Irp);
|
|
if (++Connection->DeferredPasses >= 4) {
|
|
Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_SENDS;
|
|
Connection->DeferredPasses = 0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Complete the send. Note that this may not actually call
|
|
// IoCompleteRequest for the Irp until sometime later, if the
|
|
// in-progress LLC resending going on below us needs to complete.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// Since the irp is no longer on the send list, the cancel routine
|
|
// cannot find it and will just return. We must grab the cancel
|
|
// spinlock to lock out the cancel function while we null out
|
|
// the Irp->CancelRoutine field.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
NbfCompleteSendIrp (
|
|
Irp,
|
|
STATUS_SUCCESS,
|
|
IRP_SEND_LENGTH(IrpSp));
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
++Connection->TransmittedTsdus;
|
|
|
|
if (EndOfRecord) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We've finished processing the current send. Update our state.
|
|
//
|
|
// Note: The connection spinlock is held here.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
//
|
|
// If there is another send pending on the connection, then initialize
|
|
// it and start packetizing it.
|
|
//
|
|
|
|
if (!(IsListEmpty (&Connection->SendQueue))) {
|
|
|
|
InitializeSend (Connection);
|
|
|
|
//
|
|
// This code is similar to calling StartPacketizingConnection
|
|
// with the second parameter FALSE.
|
|
//
|
|
|
|
if ((!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) &&
|
|
(Connection->Flags & CONNECTION_FLAGS_READY)) {
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE);
|
|
|
|
ExInterlockedInsertTailList(
|
|
&Connection->Provider->PacketizeQueue,
|
|
&Connection->PacketizeLinkage,
|
|
&Connection->Provider->SpinLock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// NOTE: We return with the lock held.
|
|
//
|
|
|
|
} /* CompleteSend */
|
|
|
|
|
|
VOID
|
|
FailSend(
|
|
IN PTP_CONNECTION Connection,
|
|
IN NTSTATUS RequestStatus,
|
|
IN BOOLEAN StopConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called because something on the link caused this send to be
|
|
unable to complete. There are a number of possible reasons for this to have
|
|
happened, but all will fail with the common error STATUS_LINK_FAILED.
|
|
or NO_RECEIVE response where the number of bytes specified exactly
|
|
Here we retire all of the TdiSends on the connection's SendQueue up to
|
|
and including the current one, which is the one that failed.
|
|
|
|
Later - Actually, a send failing is cause for the entire circuit to wave
|
|
goodbye to this life. We now simply tear down the connection completly.
|
|
Any future sends on this connection will be blown away.
|
|
|
|
NOTE: THIS FUNCTION MUST BE CALLED WITH THE SPINLOCK HELD.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN EndOfRecord;
|
|
BOOLEAN GotCurrent = FALSE;
|
|
KIRQL cancelIrql;
|
|
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("FailSend: Entered for connection %lx.\n", Connection);
|
|
}
|
|
|
|
|
|
//
|
|
// Pick off IRP objects from the connection's SendQueue until
|
|
// we get to this one. If this one does NOT have an EOF mark set, we'll
|
|
// need to keep going until we hit one that does have EOF set. Note that
|
|
// this may cause us to continue failing sends that have not yet been
|
|
// queued. (We do all this because NBF does not provide stream mode sends.)
|
|
//
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfReferenceConnection ("Failing Send", Connection, CREF_COMPLETE_SEND);
|
|
|
|
do {
|
|
if (IsListEmpty (&Connection->SendQueue)) {
|
|
|
|
//
|
|
// got an empty list, so we've run out of send requests to fail
|
|
// without running into an EOR. Set the connection flag that will
|
|
// cause all further sends to be failed up to an EOR and get out
|
|
// of here.
|
|
//
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_FAILING_TO_EOR;
|
|
break;
|
|
}
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
if (Irp == Connection->sp.CurrentSendIrp) {
|
|
GotCurrent = TRUE;
|
|
}
|
|
EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL);
|
|
|
|
#if DBG
|
|
NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp;
|
|
NbfCompletedSends[NbfCompletedSendsNext].Status = RequestStatus;
|
|
NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT;
|
|
#endif
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
NbfCompleteSendIrp (Irp, RequestStatus, 0);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
++Connection->TransmissionErrors;
|
|
|
|
} while (!EndOfRecord & !GotCurrent);
|
|
|
|
//
|
|
// We've finished processing the current send. Update our state.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
Connection->sp.CurrentSendIrp = NULL;
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// Blow away this connection; a failed send is a terrible thing to waste.
|
|
// Note that we are not on any packetizing queues or similar things at this
|
|
// point; we'll just disappear into the night.
|
|
//
|
|
|
|
#if MAGIC
|
|
if (NbfEnableMagic) {
|
|
extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK);
|
|
NbfSendMagicBullet (Connection->Provider, Connection->Link);
|
|
}
|
|
#endif
|
|
|
|
if (StopConnection) {
|
|
#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;
|
|
NbfPrint2( "FailSend stopping connection to %S from %S\n",
|
|
&remoteName, &localName );
|
|
}
|
|
#endif
|
|
NbfStopConnection (Connection, STATUS_LINK_FAILED);
|
|
}
|
|
|
|
#if DBG
|
|
//DbgBreakPoint ();
|
|
#endif
|
|
|
|
NbfDereferenceConnection ("FailSend", Connection, CREF_COMPLETE_SEND);
|
|
|
|
} /* FailSend */
|
|
|
|
#if DBG
|
|
//
|
|
// *** This is the original version of InitializeSend, which is now a macro.
|
|
// It has been left here as the fully-commented version of the code.
|
|
//
|
|
|
|
|
|
VOID
|
|
InitializeSend(
|
|
PTP_CONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever the next send on a connection should
|
|
be initialized; that is, all of the fields associated with the state
|
|
of the current send are set to refer to the first send on the SendQueue.
|
|
|
|
WARNING: This routine is executed with the Connection lock acquired
|
|
since it must be atomically executed with the caller's setup.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("InitializeSend: Entered for connection %lx.\n", Connection);
|
|
}
|
|
|
|
ASSERT (!IsListEmpty (&Connection->SendQueue));
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
|
Connection->FirstSendIrp =
|
|
CONTAINING_RECORD (Connection->SendQueue.Flink, IRP, Tail.Overlay.ListEntry);
|
|
Connection->FirstSendMdl = Connection->FirstSendIrp->MdlAddress;
|
|
Connection->FirstSendByteOffset = 0;
|
|
Connection->sp.MessageBytesSent = 0;
|
|
Connection->sp.CurrentSendIrp = Connection->FirstSendIrp;
|
|
Connection->sp.CurrentSendMdl = Connection->FirstSendMdl;
|
|
Connection->sp.SendByteOffset = Connection->FirstSendByteOffset;
|
|
Connection->CurrentSendLength =
|
|
IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp));
|
|
Connection->StallCount = 0;
|
|
Connection->StallBytesSent = 0;
|
|
|
|
//
|
|
// The send correlator isn't used for much; it is used so we
|
|
// can distinguish which send a piggyback ack is acking.
|
|
//
|
|
|
|
if (Connection->NetbiosHeader.ResponseCorrelator == 0xffff) {
|
|
Connection->NetbiosHeader.ResponseCorrelator = 1;
|
|
} else {
|
|
++Connection->NetbiosHeader.ResponseCorrelator;
|
|
}
|
|
|
|
} /* InitializeSend */
|
|
#endif
|
|
|
|
|
|
VOID
|
|
ReframeSend(
|
|
PTP_CONNECTION Connection,
|
|
ULONG BytesReceived
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to reset the send state variables in the connection
|
|
object to correctly point to the first byte of data to be transmitted.
|
|
In essence, this is the byte-level acknowlegement processor at the NetBIOS
|
|
level for this transport.
|
|
|
|
This is not straightforward because potentially multiple send requests
|
|
may be posted to the connection to comprise a single message. When a
|
|
send request has its TDI_END_OF_RECORD option bitflag set, then that
|
|
send is the last one to be sent in a logical message. Therefore, we
|
|
assume that the multiple-send scenario is the general case.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
BytesReceived - Number of bytes received thus far.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP Irp;
|
|
PMDL Mdl;
|
|
ULONG Offset;
|
|
ULONG BytesLeft;
|
|
ULONG MdlBytes;
|
|
PLIST_ENTRY p;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint3 ("ReframeSend: Entered for connection %lx, Flags: %lx Current Mdl: %lx\n",
|
|
Connection, Connection->Flags, Connection->sp.CurrentSendMdl);
|
|
}
|
|
|
|
//
|
|
// The caller is responsible for restarting the packetization process
|
|
// on this connection. In some cases (i.e., NO_RECEIVE handler) we
|
|
// don't want to start packetizing, so that's why we do it elsewhere.
|
|
//
|
|
|
|
//
|
|
// Examine all of the send requests and associated MDL chains starting
|
|
// with the first one at the head of the connection's SendQueue, advancing
|
|
// our complex current send pointer through the requests and MDL chains
|
|
// until we reach the byte count he's specified.
|
|
//
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
//
|
|
// In the case where a local disconnect has been issued, and we get a frame
|
|
// that causes us to reframe the send our FirstSendIrp and FirstMdl
|
|
// pointers are stale. Catch this condition and prevent faults caused by
|
|
// this. A better fix would be to change the logic that switches the
|
|
// connection sendstate from idle to W_LINK to not do that. However, this
|
|
// is a broader change than fixing it right here.
|
|
//
|
|
|
|
if (IsListEmpty(&Connection->SendQueue)) {
|
|
RELEASE_DPC_SPIN_LOCK(Connection->LinkSpinLock);
|
|
return;
|
|
}
|
|
|
|
BytesLeft = BytesReceived;
|
|
Irp = Connection->FirstSendIrp;
|
|
Mdl = Connection->FirstSendMdl;
|
|
if (Mdl) {
|
|
MdlBytes = MmGetMdlByteCount (Mdl);
|
|
} else {
|
|
MdlBytes = 0; // zero-length send
|
|
}
|
|
Offset = Connection->FirstSendByteOffset;
|
|
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TRACKTDI) {
|
|
NbfPrint3 ("ReFrameSend: Called with Connection %lx FirstSend %lx CurrentSend %lx\n",
|
|
Connection, Connection->FirstSendIrp, Connection->sp.CurrentSendIrp);
|
|
Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_SENDS;
|
|
Connection->DeferredPasses = 0;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We loop through while we have acked bytes left to account for,
|
|
// advancing our pointers and completing any sends that have been
|
|
// completely acked.
|
|
//
|
|
|
|
while (BytesLeft != 0) {
|
|
|
|
if (Mdl == NULL) {
|
|
KIRQL cancelIrql;
|
|
|
|
//
|
|
// We have exhausted the MDL chain on this request, so it has
|
|
// been implicitly acked. That means we must complete the I/O
|
|
// by dereferencing the request before we reframe further.
|
|
//
|
|
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
|
|
//
|
|
// Since the irp is no longer on the list, the cancel routine
|
|
// won't find it. Grab the cancel spinlock to synchronize
|
|
// and complete the irp.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
NbfCompleteSendIrp (Irp, STATUS_SUCCESS, Offset);
|
|
|
|
//
|
|
// Now continue with the next request in the list.
|
|
//
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
p = Connection->SendQueue.Flink;
|
|
if (p == &Connection->SendQueue) {
|
|
|
|
ULONG DumpData[2];
|
|
|
|
//
|
|
// The byte acknowledgement was for more than the
|
|
// total length of sends we have outstanding; to
|
|
// avoid problems we tear down the connection.
|
|
//
|
|
#if DBG
|
|
NbfPrint2 ("NbfReframeSend: Got %d extra bytes acked on %lx\n",
|
|
BytesLeft, Connection);
|
|
ASSERT (FALSE);
|
|
#endif
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
DumpData[0] = Offset;
|
|
DumpData[1] = BytesLeft;
|
|
|
|
NbfWriteGeneralErrorLog(
|
|
Connection->Provider,
|
|
EVENT_TRANSPORT_BAD_PROTOCOL,
|
|
1,
|
|
STATUS_INVALID_NETWORK_RESPONSE,
|
|
L"REFRAME",
|
|
2,
|
|
DumpData);
|
|
|
|
NbfStopConnection (Connection, STATUS_INVALID_NETWORK_RESPONSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
Mdl = Irp->MdlAddress;
|
|
MdlBytes = MmGetMdlByteCount (Mdl);
|
|
Offset = 0;
|
|
|
|
} else if (MdlBytes > (Offset + BytesLeft)) {
|
|
|
|
//
|
|
// This MDL has more data than we really need. Just use
|
|
// part of it. Then get out, because we're done.
|
|
//
|
|
|
|
Offset += BytesLeft;
|
|
BytesLeft = 0;
|
|
break;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This MDL does not have enough data to satisfy the ACK, so
|
|
// use as much data as it has, and cycle around again.
|
|
//
|
|
|
|
Offset = 0;
|
|
BytesLeft -= MdlBytes;
|
|
Mdl = Mdl->Next;
|
|
|
|
if (Mdl != NULL) {
|
|
MdlBytes = MmGetMdlByteCount (Mdl);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tmp debugging; we want to see if we got byte acked
|
|
// for the entire send. This will break if we have
|
|
// non-EOR sends.
|
|
//
|
|
|
|
#if DBG
|
|
if (BytesReceived != 0) {
|
|
ASSERTMSG ("NbfReframeSend: Byte ack for entire send\n",
|
|
Mdl != NULL);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We've acked some data, possibly on a byte or message boundary.
|
|
// We must pretend we're sending a new message all over again,
|
|
// starting with the byte immediately after the last one he acked.
|
|
//
|
|
|
|
Connection->FirstSendIrp = Irp;
|
|
Connection->FirstSendMdl = Mdl;
|
|
Connection->FirstSendByteOffset = Offset;
|
|
|
|
//
|
|
// Since we haven't started sending this new reframed message yet,
|
|
// we set our idea of the current complex send pointer to the first
|
|
// complex send pointer.
|
|
//
|
|
|
|
Connection->sp.MessageBytesSent = 0;
|
|
Connection->sp.CurrentSendIrp = Irp;
|
|
Connection->sp.CurrentSendMdl = Mdl;
|
|
Connection->sp.SendByteOffset = Offset;
|
|
Connection->CurrentSendLength -= BytesReceived;
|
|
Connection->StallCount = 0;
|
|
Connection->StallBytesSent = 0;
|
|
|
|
#if DBG
|
|
IF_NBFDBG (NBF_DEBUG_TRACKTDI) {
|
|
|
|
{
|
|
PLIST_ENTRY p;
|
|
NbfPrint0 ("ReFrameSend: Walking Send List:\n");
|
|
|
|
for (
|
|
p = Connection->SendQueue.Flink;
|
|
p != &Connection->SendQueue;
|
|
p=p->Flink ) {
|
|
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
NbfPrint1 (" Irp %lx\n", Irp);
|
|
}
|
|
}}
|
|
#endif
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
|
|
} /* ReframeSend */
|
|
|
|
|
|
VOID
|
|
NbfCancelSend(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to cancel a send.
|
|
The send is found on the connection's send queue; if it is the
|
|
current request it is cancelled and the connection is torn down,
|
|
otherwise it is silently cancelled.
|
|
|
|
NOTE: This routine is called with the CancelSpinLock held and
|
|
is responsible for releasing it.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for this driver.
|
|
|
|
Irp - Pointer to the request packet representing the I/O request.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql1;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PTP_CONNECTION Connection;
|
|
PIRP SendIrp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN Found;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP. This is where
|
|
// the function codes and parameters are stored.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
|
(IrpSp->MinorFunction == TDI_SEND));
|
|
|
|
Connection = IrpSp->FileObject->FsContext;
|
|
|
|
//
|
|
// Since this IRP is still in the cancellable state, we know
|
|
// that the connection is still around (although it may be in
|
|
// the process of being torn down).
|
|
//
|
|
|
|
|
|
//
|
|
// See if this is the IRP for the current send request.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (Connection->LinkSpinLock, &oldirql);
|
|
NbfReferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND);
|
|
|
|
p = Connection->SendQueue.Flink;
|
|
SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
|
|
if (SendIrp == Irp) {
|
|
|
|
//
|
|
// yes, it is the first one on the send queue, so
|
|
// trash the send/connection. The first send is a special case
|
|
// there are multiple pointers to the send request. Just stop the
|
|
// connection.
|
|
//
|
|
|
|
// p = RemoveHeadList (&Connection->SendQueue);
|
|
|
|
#if DBG
|
|
NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp;
|
|
NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED;
|
|
NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT;
|
|
#endif
|
|
|
|
//
|
|
// Prevent anyone from getting in to packetize before we
|
|
// call NbfStopConnection.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
#if DBG
|
|
DbgPrint("NBF: Canceled in-progress send %lx on %lxn",
|
|
SendIrp, Connection);
|
|
#endif
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldirql1);
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
// NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0);
|
|
|
|
//
|
|
// Since we are cancelling the current send, blow away
|
|
// the connection.
|
|
//
|
|
|
|
NbfStopConnection (Connection, STATUS_CANCELLED);
|
|
|
|
KeLowerIrql (oldirql1);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Scan through the list, looking for this IRP. If we
|
|
// cancel anything up to the first EOR on the list
|
|
// we still tear down the connection since this would
|
|
// mess up our packetizing otherwise. We set CancelledFirstEor
|
|
// to FALSE when we pass an IRP without SEND_PARTIAL.
|
|
//
|
|
// NO MATTER WHAT WE MUST SHUT DOWN THE CONNECTION!!!!
|
|
|
|
#if 0
|
|
if (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL)) {
|
|
CancelledFirstEor = FALSE;
|
|
} else {
|
|
CancelledFirstEor = TRUE;
|
|
}
|
|
#endif
|
|
|
|
Found = FALSE;
|
|
p = p->Flink;
|
|
while (p != &Connection->SendQueue) {
|
|
|
|
SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
if (SendIrp == Irp) {
|
|
|
|
//
|
|
// Found it, remove it from the list here.
|
|
//
|
|
|
|
RemoveEntryList (p);
|
|
|
|
Found = TRUE;
|
|
|
|
#if DBG
|
|
NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp;
|
|
NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED;
|
|
NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT;
|
|
#endif
|
|
|
|
RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
#if DBG
|
|
DbgPrint("NBF: Canceled queued send %lx on %lx\n",
|
|
SendIrp, Connection);
|
|
#endif
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldirql1);
|
|
|
|
NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0);
|
|
//
|
|
// STOP THE CONNECTION NO MATTER WHAT!!!
|
|
//
|
|
NbfStopConnection (Connection, STATUS_CANCELLED);
|
|
|
|
KeLowerIrql (oldirql1);
|
|
break;
|
|
|
|
}
|
|
#if 0
|
|
else {
|
|
|
|
if (CancelledFirstEor && (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL))) {
|
|
CancelledFirstEor = FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
p = p->Flink;
|
|
|
|
}
|
|
|
|
if (!Found) {
|
|
|
|
//
|
|
// We didn't find it!
|
|
//
|
|
|
|
#if DBG
|
|
DbgPrint("NBF: Tried to cancel send %lx on %lx, not found\n",
|
|
Irp, Connection);
|
|
#endif
|
|
RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
}
|
|
|
|
}
|
|
|
|
NbfDereferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND);
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
ResendPacket (
|
|
PTP_LINK Link,
|
|
PTP_PACKET Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resends a packet on the link. Since this is a resend, we
|
|
are careful to not reset the state unless all resends have completed.
|
|
|
|
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Link - Pointer to a TP_LINK object.
|
|
|
|
Packet - pointer to packet to be resent.
|
|
|
|
Return Value:
|
|
|
|
True if resending should continue; FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN PollFinal;
|
|
PDLC_I_FRAME DlcHeader;
|
|
UINT DataLength;
|
|
|
|
|
|
//
|
|
|
|
DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]);
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint3 ("ReSendPacket: %lx NdisPacket: %lx # %x\n",
|
|
Packet, Packet->NdisPacket,
|
|
DlcHeader->RcvSeq >>1);
|
|
IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) {
|
|
{PUCHAR q;
|
|
USHORT i;
|
|
q = Packet->Header;
|
|
for (i=0;i<20;i++) {
|
|
NbfPrint1 (" %2x",q[i]);
|
|
}
|
|
NbfPrint0 ("\n");}
|
|
}
|
|
}
|
|
|
|
DataLength = Packet->NdisIFrameLength;
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
Link->WindowErrors++;
|
|
|
|
PollFinal = (BOOLEAN)((DlcHeader->RcvSeq & DLC_I_PF) != 0);
|
|
|
|
StopT2 (Link); // since this is potentially acking some frames
|
|
|
|
if (Link->Provider->MacInfo.MediumAsync) {
|
|
if (PollFinal) {
|
|
ASSERT (Packet->Link != NULL);
|
|
NbfReferenceLink ("ResendPacket", Link, LREF_START_T1);
|
|
} else {
|
|
StartT1 (Link, 0);
|
|
}
|
|
} else {
|
|
StartT1 (Link, PollFinal ? DataLength : 0); // restart transmission timer
|
|
}
|
|
|
|
//
|
|
// Update the expected next receive in case it's changed
|
|
//
|
|
|
|
if (PollFinal) {
|
|
|
|
DlcHeader->RcvSeq = DLC_I_PF; // set the poll bit.
|
|
Link->SendState = SEND_STATE_CHECKPOINTING;
|
|
|
|
Link->ResendingPackets = FALSE;
|
|
|
|
} else {
|
|
|
|
DlcHeader->RcvSeq = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// DlcHeader->RcvSeq has Link->NextReceive inserted by NbfNdisSend.
|
|
//
|
|
|
|
NbfReferencePacket (Packet); // so we don't remove it in send completion
|
|
|
|
NbfReferenceLink ("ResendPacket", Link, LREF_NDIS_SEND);
|
|
|
|
ASSERT (Packet->PacketSent == TRUE);
|
|
Packet->PacketSent = FALSE;
|
|
|
|
//
|
|
// Update our "bytes resent" counters.
|
|
//
|
|
|
|
DataLength -=
|
|
Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION);
|
|
|
|
|
|
ADD_TO_LARGE_INTEGER(
|
|
&Link->Provider->Statistics.DataFrameBytesResent,
|
|
DataLength);
|
|
++Link->Provider->Statistics.DataFramesResent;
|
|
|
|
|
|
//
|
|
// Send the packet (this release the link spinlock).
|
|
//
|
|
|
|
NbfNdisSend (Link, Packet);
|
|
|
|
++Link->PacketsResent;
|
|
|
|
NbfDereferenceLink ("ResendPacket", Link, LREF_NDIS_SEND);
|
|
|
|
//
|
|
// if this packet has POLL set, stop the resending so the
|
|
// link doesn't get all twisted up.
|
|
//
|
|
|
|
if (PollFinal) {
|
|
|
|
//
|
|
// so we're in the state of having sent a poll and not
|
|
// sending anything else until we get a final. This avoids
|
|
// overrunning the remote. Note that we leave the routine
|
|
// with state LINK_SENDSTATE_REJECTING, which guarentees
|
|
// we won't start any new sends until we traverse through
|
|
// this routine again.
|
|
//
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
ResendLlcPackets (
|
|
PTP_LINK Link,
|
|
UCHAR AckSequenceNumber,
|
|
BOOLEAN Resend
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine advances the state of a data link connection by retiring
|
|
all of the packets on the link's WackQ that have send sequence numbers
|
|
logically less than that number specified as the AckSequenceNumber, and
|
|
resending those above that number. The packets are disposed of by
|
|
dereferencing them. We cannot simply destroy them because this
|
|
acknowlegement might arrive even before the Physical Provider has had a
|
|
chance to issue a completion event for the associated I/O.
|
|
|
|
NOTE: This function is called with the link spinlock held and
|
|
returns with it held, but it may release it in between. THIS
|
|
ROUTINE MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Link - Pointer to a TP_LINK object.
|
|
|
|
AckSequenceNumber - An unsigned number specifing the sequence number of
|
|
the first packet within the window that is NOT acknowleged.
|
|
|
|
Resend - if TRUE, resend packets. If FALSE, just remove them from the
|
|
wackq and get out.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_PACKET packet;
|
|
PLIST_ENTRY p, p1;
|
|
UCHAR packetSeq;
|
|
BOOLEAN passedAck = FALSE;
|
|
PDLC_I_FRAME DlcHeader;
|
|
SCHAR Difference;
|
|
BOOLEAN ReturnValue = FALSE;
|
|
// NDIS_STATUS ndisStatus;
|
|
|
|
//
|
|
// Move through the queue, releasing those we've been acked for and resending
|
|
// others above that.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint3 ("ResendLlcPackets: Link %lx, Ack: %x, LinkLastAck: %x.\n",
|
|
Link, AckSequenceNumber, Link->LastAckReceived);
|
|
NbfPrint0 ("RLP: Walking WackQ, Packets:\n");
|
|
p = Link->WackQ.Flink; // p = ptr, 1st pkt's linkage.
|
|
while (p != &Link->WackQ) {
|
|
packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
|
|
DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]);
|
|
NbfPrint4 ("RLP: Pkt: %lx # %x Flags: %d %d\n", packet,
|
|
(UCHAR)(DlcHeader->SendSeq >> 1), packet->PacketSent, packet->PacketNoNdisBuffer);
|
|
p = packet->Linkage.Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If somebody else is resending LLC packets (which means they
|
|
// are in this function with Resend == TRUE), then ignore
|
|
// this frame. This is because it may ack a frame that he
|
|
// is in the middle of resending, which will cause problems.
|
|
//
|
|
// This isn't a great solution, we should keep track
|
|
// of where the other guy is and avoid stepping on him. This
|
|
// might mess up his walking of the queue however.
|
|
//
|
|
|
|
if (Link->ResendingPackets) {
|
|
NbfPrint1("ResendLlcPackets: Someone else resending on %lx\n", Link);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// We have already checked that AckSequenceNumber is reasonable.
|
|
//
|
|
|
|
Link->LastAckReceived = AckSequenceNumber;
|
|
|
|
if (Resend) {
|
|
|
|
//
|
|
// Only one person can be resending or potentially resending
|
|
// at one time.
|
|
//
|
|
|
|
Link->ResendingPackets = TRUE;
|
|
}
|
|
|
|
//
|
|
// Resend as many packets as we have window to send. We spin through the
|
|
// queue and remove those packets that have been acked or that are
|
|
// sequence numbered logically below the current ack number. The flags
|
|
// PACKET_FLAGS_RESEND and PACKET_FLAGS_SENT correspond to the three states
|
|
// a packet on this queue can be in:
|
|
//
|
|
// 1) if _RESEND is set, the packet has not been acked
|
|
//
|
|
// 2) if _SENT is set, the packet send has completed (conversely, if NOT
|
|
// set, the packet has not yet been completely sent, thus it is
|
|
// unnecessary to resend it).
|
|
// 3) if _RESEND and _SENT are both set, the packet has been sent and not
|
|
// acked and is grist for our mills.
|
|
// 4) if neither is set, the world is coming to an end next Thursday.
|
|
//
|
|
|
|
p=Link->WackQ.Flink;
|
|
while (p != &Link->WackQ) {
|
|
packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
|
|
DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]);
|
|
|
|
//
|
|
// if both bits aren't set we can't do a thing with this packet, or,
|
|
// for that matter, with the rest of the packet list. We can't
|
|
// have reached the ack number yet, as these packets haven't even
|
|
// completed sending.
|
|
// (Later) actually, we can have reached passedAck, and if we did
|
|
// we're in a world of hurt. We can't send more regular packets,
|
|
// but we can't send any resend packets either. Force the link to
|
|
// checkpoint and things will clear themselves up later.
|
|
//
|
|
|
|
if (!(packet->PacketSent)) {
|
|
if (passedAck) {
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint2 ("ResendLLCPacket: Can't send WACKQ Packet RcvSeq %x %x \n",
|
|
DlcHeader->RcvSeq, DlcHeader->SendSeq);
|
|
}
|
|
|
|
if (Link->SendState != SEND_STATE_CHECKPOINTING) {
|
|
|
|
//
|
|
// Don't start checkpointing if we already are.
|
|
//
|
|
|
|
Link->SendState = SEND_STATE_CHECKPOINTING;
|
|
StopTi (Link);
|
|
StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // start checkpoint timeout.
|
|
Link->ResendingPackets = FALSE;
|
|
|
|
//
|
|
// Try this...in this case don't actually send
|
|
// an RR, since his response might put us right
|
|
// back here. When T1 expires we will recover.
|
|
//
|
|
// NbfSendRr (Link, TRUE, TRUE);
|
|
|
|
} else {
|
|
|
|
Link->ResendingPackets = FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Don't break, since passedAck is FALSE all we will
|
|
// do in the next section is TpDereferencePacket, which
|
|
// is correct.
|
|
//
|
|
// break;
|
|
}
|
|
|
|
//
|
|
// This loop is somewhat schizo; at this point, if we've not yet reached
|
|
// the ack number, we'll be ditching the packet. If we've gone through
|
|
// the ack number, we'll be re-transmitting. Note that in the first
|
|
// personality, we are always looking at the beginning of the list.
|
|
//
|
|
|
|
//
|
|
// NOTE: Link spinlock is held here.
|
|
//
|
|
|
|
packetSeq = (UCHAR)(DlcHeader->SendSeq >> 1);
|
|
if (!passedAck){
|
|
|
|
//
|
|
// Compute the signed difference here; see if
|
|
// packetSeq is equal to or "greater than"
|
|
// LastAckReceived.
|
|
//
|
|
|
|
Difference = packetSeq - Link->LastAckReceived;
|
|
|
|
if (((Difference >= 0) && (Difference < 0x40)) ||
|
|
(Difference < -0x40)) {
|
|
|
|
//
|
|
// We have found a packet on the queue that was
|
|
// not acknowledged by LastAckReceived.
|
|
//
|
|
|
|
if (Link->SendState == SEND_STATE_CHECKPOINTING) {
|
|
|
|
//
|
|
// If we are checkpointing, we should not do any of
|
|
// the passedAck things (i.e. any of the things which
|
|
// potentially involve sending packets) - adb 7/30/91.
|
|
//
|
|
|
|
if (Resend) {
|
|
Link->ResendingPackets = FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
if (!Resend) {
|
|
|
|
//
|
|
// If we are not supposed to resend, then exit.
|
|
// Since there are still packets on the queue
|
|
// we restart T1.
|
|
//
|
|
|
|
StopTi (Link);
|
|
StartT1 (Link, 0); // start checkpoint timeout.
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Lock out senders, so we maintain packet sequences properly
|
|
//
|
|
|
|
Link->SendState = SEND_STATE_REJECTING; // we're resending.
|
|
|
|
passedAck = TRUE;
|
|
|
|
//
|
|
// Note that we don't advance the pointer to the next packet;
|
|
// thus, we will resend this packet on the next pass through
|
|
// the while loop (taking the passedAck branch).
|
|
//
|
|
|
|
} else {
|
|
p1 = RemoveHeadList (&Link->WackQ);
|
|
ASSERTMSG (" ResendLLCPacket: Packet not at queue head!\n", (p == p1));
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
ReturnValue = TRUE;
|
|
NbfDereferencePacket (packet);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
p = Link->WackQ.Flink;
|
|
}
|
|
|
|
} else {
|
|
// NbfPrint1 ("RLP: # %x\n",packetSeq);
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
//
|
|
// If this call returns FALSE (because we checkpoint)
|
|
// it clears ResendingPacket before it returns.
|
|
//
|
|
|
|
if (!ResendPacket (Link, packet)) {
|
|
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
return ReturnValue;
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
p = p->Flink;
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE: Link spinlock is held here.
|
|
//
|
|
|
|
if (passedAck) {
|
|
|
|
//
|
|
// If we exit through here with passedAck TRUE, it means that we
|
|
// successfully called ResendPacket on every packet in the
|
|
// WackQ, which means we did not resend a poll packet, so we
|
|
// can start sending normally again. We have to clear
|
|
// ResendingPackets here.
|
|
//
|
|
|
|
Link->SendState = SEND_STATE_READY;
|
|
Link->ResendingPackets = FALSE;
|
|
StartTi (Link);
|
|
|
|
} else if (!Resend) {
|
|
|
|
//
|
|
// If Resend is FALSE (in which case passedAck will also be FALSE,
|
|
// by the way), and the WackQ is empty, that means that we
|
|
// successfully acknowledged all the packets on a non-final
|
|
// frame. In this case T1 may be running, but in fact is not
|
|
// needed since there are no sends outstanding.
|
|
//
|
|
|
|
if (Link->WackQ.Flink == &Link->WackQ) {
|
|
StopT1 (Link);
|
|
}
|
|
Link->SendState = SEND_STATE_READY;
|
|
StartTi (Link);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Resend is TRUE, but passedAck is FALSE; we came in
|
|
// expecting to resend, but didn't. This means that
|
|
// we have emptied the queue after receiving an
|
|
// RR/f, i.e. this send window is done and we can
|
|
// update our send window size, etc.
|
|
//
|
|
|
|
Link->ResendingPackets = FALSE;
|
|
|
|
if (Link->Provider->MacInfo.MediumAsync) {
|
|
return ReturnValue;
|
|
}
|
|
|
|
if (Link->WindowErrors > 0) {
|
|
|
|
//
|
|
// We had transmit errors on this window.
|
|
//
|
|
|
|
Link->PrevWindowSize = Link->SendWindowSize;
|
|
|
|
//
|
|
// We use 100 ms delay as the cutoff for a LAN.
|
|
//
|
|
|
|
if (Link->Delay < (100*MILLISECONDS)) {
|
|
|
|
//
|
|
// On a LAN, if we have a special case
|
|
// if one packet was lost; this means the
|
|
// final packet was retransmitted once. In
|
|
// that case, we keep track of Consecutive
|
|
// LastPacketLost, and if it reaches 2, then
|
|
// we lock the send window at its current
|
|
// value minus one.
|
|
//
|
|
|
|
if (Link->WindowErrors == 1) {
|
|
|
|
++Link->ConsecutiveLastPacketLost;
|
|
|
|
if (Link->ConsecutiveLastPacketLost >= 2) {
|
|
|
|
//
|
|
// Freeze the window wherever it was.
|
|
//
|
|
|
|
if (Link->SendWindowSize > Link->Provider->MinimumSendWindowLimit) {
|
|
Link->MaxWindowSize = Link->SendWindowSize - 1;
|
|
Link->SendWindowSize = (UCHAR)Link->MaxWindowSize;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Otherwise, we leave the window where it is.
|
|
//
|
|
|
|
} else {
|
|
|
|
Link->ConsecutiveLastPacketLost = 0;
|
|
Link->SendWindowSize -= (UCHAR)Link->WindowErrors;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// On a WAN we cut the send window in half,
|
|
// regardless of how many frames were retransmitted.
|
|
//
|
|
|
|
Link->SendWindowSize /= 2;
|
|
Link->WindowsUntilIncrease = 1; // in case Prev is also 1.
|
|
Link->ConsecutiveLastPacketLost = 0;
|
|
|
|
}
|
|
|
|
if ((SCHAR)Link->SendWindowSize < 1) {
|
|
Link->SendWindowSize = 1;
|
|
}
|
|
|
|
//
|
|
// Reset our counters for the next window.
|
|
//
|
|
|
|
Link->WindowErrors = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have successfully sent a window of data, increase
|
|
// the send window size unless we are at the limit.
|
|
// We use 100 ms delay as the WAN/LAN cutoff.
|
|
//
|
|
|
|
if ((ULONG)Link->SendWindowSize < Link->MaxWindowSize) {
|
|
|
|
if (Link->Delay < (100*MILLISECONDS)) {
|
|
|
|
//
|
|
// On a LAN, increase the send window by 1.
|
|
//
|
|
// Need to determine optimal window size.
|
|
//
|
|
|
|
Link->SendWindowSize++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// On a WAN, increase the send window by 1 until
|
|
// we hit PrevWindowSize, then do it more slowly.
|
|
//
|
|
|
|
if (Link->SendWindowSize < Link->PrevWindowSize) {
|
|
|
|
Link->SendWindowSize++;
|
|
|
|
//
|
|
// If we just increased it to the previous window
|
|
// size, prepare for the next time through here.
|
|
//
|
|
|
|
if (Link->SendWindowSize == Link->PrevWindowSize) {
|
|
Link->WindowsUntilIncrease = Link->SendWindowSize;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We passed the previous size, so only update every
|
|
// WindowsUntilIncrease times.
|
|
//
|
|
|
|
if (--Link->WindowsUntilIncrease == 0) {
|
|
|
|
Link->SendWindowSize++;
|
|
Link->WindowsUntilIncrease = Link->SendWindowSize;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ULONG)Link->SendWindowSize > Link->Provider->Statistics.MaximumSendWindow) {
|
|
Link->Provider->Statistics.MaximumSendWindow = Link->SendWindowSize;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Clear this since we had no errors.
|
|
//
|
|
|
|
Link->ConsecutiveLastPacketLost = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ReturnValue;
|
|
|
|
} /* ResendLlcPackets */
|
|
|
|
|
|
VOID
|
|
NbfSendCompletionHandler(
|
|
IN NDIS_HANDLE ProtocolBindingContext,
|
|
IN PNDIS_PACKET NdisPacket,
|
|
IN NDIS_STATUS NdisStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to indicate that a connection-
|
|
oriented packet has been shipped and is no longer needed by the Physical
|
|
Provider.
|
|
|
|
Arguments:
|
|
|
|
NdisContext - the value associated with the adapter binding at adapter
|
|
open time (which adapter we're talking on).
|
|
|
|
NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent.
|
|
|
|
NdisStatus - the completion status of the send.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSEND_PACKET_TAG SendContext;
|
|
PTP_PACKET Packet;
|
|
KIRQL oldirql1;
|
|
ProtocolBindingContext; // avoid compiler warnings
|
|
|
|
#if DBG
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
NbfSendsCompletedAfterPendFail++;
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n",
|
|
NdisPacket, NbfGetNdisStatus (NdisStatus));
|
|
}
|
|
} else {
|
|
NbfSendsCompletedAfterPendOk++;
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n",
|
|
NdisPacket, NbfGetNdisStatus (NdisStatus));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0];
|
|
|
|
switch (SendContext->Type) {
|
|
case TYPE_I_FRAME:
|
|
|
|
//
|
|
// Just dereference the packet. There are a couple possibilities here.
|
|
// First, the I/O completion might happen before an ACK is received,
|
|
// in which case this will remove one of the references, but not both.
|
|
// Second, the LLC ACK for this packet may have already been processed,
|
|
// in which case this will destroy the packet. Third, this packet may
|
|
// be resent, either before or after this call, in which case the deref
|
|
// won't destroy the packet.
|
|
//
|
|
// NbfDereferencePacket will call PacketizeSend if it determines that
|
|
// there is at least one connection waiting to be packetized because
|
|
// of out-of-resource conditions or because its window has been opened.
|
|
//
|
|
|
|
Packet = ((PTP_PACKET)SendContext->Frame);
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldirql1);
|
|
|
|
if (Packet->Provider->MacInfo.MediumAsync) {
|
|
|
|
if (Packet->Link) {
|
|
|
|
ASSERT (Packet->NdisIFrameLength > 0);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&Packet->Link->SpinLock);
|
|
StartT1 (Packet->Link, Packet->NdisIFrameLength);
|
|
RELEASE_DPC_SPIN_LOCK (&Packet->Link->SpinLock);
|
|
|
|
NbfDereferenceLink ("Send completed", Packet->Link, LREF_START_T1);
|
|
}
|
|
|
|
if (Packet->PacketizeConnection) {
|
|
|
|
PTP_CONNECTION Connection = IRP_SEND_CONNECTION((PIO_STACK_LOCATION)(Packet->Owner));
|
|
PDEVICE_CONTEXT DeviceContext = Packet->Provider;
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) &&
|
|
(Connection->Flags & CONNECTION_FLAGS_READY)) {
|
|
|
|
ASSERT (Connection->Flags & CONNECTION_FLAGS_PACKETIZE);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->SpinLock);
|
|
|
|
NbfReferenceConnection ("Delayed packetizing", Connection, CREF_PACKETIZE_QUEUE);
|
|
InsertTailList(&DeviceContext->PacketizeQueue, &Connection->PacketizeLinkage);
|
|
|
|
if (!DeviceContext->WanThreadQueued) {
|
|
|
|
DeviceContext->WanThreadQueued = TRUE;
|
|
ExQueueWorkItem(&DeviceContext->WanDelayedQueueItem, DelayedWorkQueue);
|
|
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
|
|
} else {
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfDereferenceConnection ("PacketizeConnection FALSE", Connection, CREF_TEMP);
|
|
|
|
Packet->PacketizeConnection = FALSE;
|
|
|
|
}
|
|
}
|
|
#if DBG
|
|
if (Packet->PacketSent) {
|
|
DbgPrint ("NbfSendCompletionHandler: Packet %lx already completed\n", Packet);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
Packet->PacketSent = TRUE;
|
|
|
|
NbfDereferencePacket (Packet);
|
|
|
|
KeLowerIrql (oldirql1);
|
|
break;
|
|
|
|
case TYPE_UI_FRAME:
|
|
|
|
//
|
|
// just destroy the frame; name stuff doesn't depend on having any
|
|
// of the sent message left around after the send completed.
|
|
//
|
|
|
|
NbfDestroyConnectionlessFrame ((PDEVICE_CONTEXT)SendContext->Owner,
|
|
(PTP_UI_FRAME)SendContext->Frame);
|
|
break;
|
|
|
|
case TYPE_ADDRESS_FRAME:
|
|
|
|
//
|
|
// Addresses get their own frames; let the address know it's ok to
|
|
// use the frame again.
|
|
//
|
|
|
|
NbfSendDatagramCompletion ((PTP_ADDRESS)SendContext->Owner,
|
|
NdisPacket,
|
|
NdisStatus );
|
|
break;
|
|
}
|
|
|
|
return;
|
|
|
|
} /* NbfSendCompletionHandler */
|
|
|
|
|
|
NTSTATUS
|
|
SendOnePacket(
|
|
IN PTP_CONNECTION Connection,
|
|
IN PTP_PACKET Packet,
|
|
IN BOOLEAN ForceAck,
|
|
OUT PBOOLEAN LinkCheckpoint OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a connection-oriented packet by calling the NDIS
|
|
Send service. At least one event will occur following
|
|
(or during) the Send request's processing. (1) The Send request
|
|
will complete through the I/O system, calling IoCompleteRequest.
|
|
(2) The sequenced packet will be acknowleged at the LLC level, or it
|
|
will be rejected and reset at the LLC level. If the packet is resent,
|
|
then it remains queued at the TP_LINK object. If the packet is ACKed,
|
|
then is removed from the link's WackQ and the Action field in the
|
|
TP_PACKET structure dictates what operation to perform next.
|
|
|
|
NOTE: This routine is called with the link spinlock held. THIS
|
|
ROUTINE MUST BE CALLED AT DPC LEVEL.
|
|
|
|
NOTE: This routine will now accept all frames unless the link
|
|
is down. If the link cannot send, the packet will be queued and
|
|
sent when possible.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Packet - Pointer to a TP_PACKET object.
|
|
|
|
ForceAck - Boolean that, if true, indicates this packet should always have
|
|
the Poll bit set; this force the other side to ack immediately,
|
|
which is necessary for correct session teardown.
|
|
|
|
LinkCheckpoint - If specified, will return TRUE if the link has
|
|
just entered a checkpoint state. In this case the status
|
|
will be STATUS_SUCCESS, but the connection should stop
|
|
packetizing now (in fact, to close a window, the connection
|
|
is put into the W_LINK state if this status will be
|
|
returned, so he must stop because somebody else may
|
|
already be doing it).
|
|
|
|
Return Value:
|
|
|
|
STATUS_LINK_FAILED - the link is dead or not ready.
|
|
STATUS_SUCCESS - the packet has been sent.
|
|
STATUS_INSUFFICIENT_RESOURCES - the packet has been queued.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_LINK Link;
|
|
PDLC_I_FRAME DlcHeader;
|
|
PNDIS_BUFFER ndisBuffer;
|
|
ULONG SendsOutstanding;
|
|
BOOLEAN Poll = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_PACKET) {
|
|
NbfPrint3 ("SendOnePacket: Entered, connection %lx, packet %lx DnisPacket %lx.\n",
|
|
Connection, Packet, Packet->NdisPacket);
|
|
}
|
|
|
|
Link = Connection->Link;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_PACKET) {
|
|
UINT PLength, PCount;
|
|
UINT BLength;
|
|
PVOID BAddr;
|
|
NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength);
|
|
NbfPrint3 ("Sending Data Packet: %lx, Length: %lx Pages: %lx\n",
|
|
Packet->NdisPacket, PLength, PCount);
|
|
while (ndisBuffer != NULL) {
|
|
NdisQueryBuffer(ndisBuffer, &BAddr, &BLength);
|
|
NbfPrint3 ("Sending Data Packet: Buffer %08lx Length %08lx Va %08lx\n",
|
|
ndisBuffer, BLength, BAddr);
|
|
NdisGetNextBuffer (ndisBuffer, &ndisBuffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the general state of the link is not READY, then we can't ship.
|
|
// This failure can be expected under some conditions, and may not cause
|
|
// failure of the send.
|
|
//
|
|
|
|
if (Link->State != LINK_STATE_READY) {
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("SendOnePacket: Link state is not READY (%ld).\n", Link->State);
|
|
}
|
|
|
|
//
|
|
// determine what to do with this problem. If we shouldn't be sending
|
|
// here, percolate an error upward.
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint3 ("SendOnePacket: Link Bad state, link: %lx Link Flags %lx Link State %lx\n",
|
|
Link, Link->Flags, Link->State);
|
|
}
|
|
return STATUS_LINK_FAILED;
|
|
}
|
|
|
|
|
|
SendsOutstanding = (((ULONG)Link->NextSend+128L-(ULONG)Link->LastAckReceived)%128L);
|
|
|
|
//
|
|
// Format LLC header while we've got the spinlock to atomically update
|
|
// the link's state information.
|
|
//
|
|
|
|
DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]);
|
|
DlcHeader->SendSeq = (UCHAR)(Link->NextSend << 1);
|
|
Link->NextSend = (UCHAR)((Link->NextSend + 1) & 0x7f);
|
|
DlcHeader->RcvSeq = 0; // Link->NextReceive is inserted by NbfNdisSend
|
|
|
|
//
|
|
// Before we release the spinlock, we append the packet to the
|
|
// end of the link's WackQ, so that if an ACK arrives before the NdisSend
|
|
// completes, it will be on the queue already. Also, mark the packet as
|
|
// needing resend, which is canceled by AckLLCPackets, and used by
|
|
// ResendLLCPackets. Thus, all packets will need to be resent until they
|
|
// are acked.
|
|
//
|
|
|
|
ASSERT (Packet->PacketSent == FALSE);
|
|
|
|
InsertTailList (&Link->WackQ, &Packet->Linkage);
|
|
//SrvCheckListIntegrity( &Link->WackQ, 200 );
|
|
|
|
|
|
//
|
|
// If the send state is not READY, we can't ship.
|
|
// This failure is mostly caused by flow control or retransmit in progress,
|
|
// and is never cause for failure of the send.
|
|
//
|
|
|
|
if ((Link->SendState != SEND_STATE_READY) ||
|
|
(Link->LinkBusy) ||
|
|
(SendsOutstanding >= (ULONG)Link->SendWindowSize)) {
|
|
|
|
if ((Link->SendWindowSize == 1) || ForceAck) {
|
|
DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit.
|
|
if (Link->Provider->MacInfo.MediumAsync) {
|
|
Packet->Link = Link;
|
|
}
|
|
}
|
|
|
|
Packet->PacketSent = TRUE; // allows it to be resent.
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
#if DBG
|
|
if (Link->SendState != SEND_STATE_READY) {
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("SendOnePacket: Link send state not READY (%ld).\n", Link->SendState);
|
|
}
|
|
} else if (Link->LinkBusy) {
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
PANIC ("SendOnePacket: Link is busy.\n");
|
|
}
|
|
} else if (SendsOutstanding >= (ULONG)Link->SendWindowSize) {
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint3 ("SendOnePacket: No link send window; N(S)=%ld,LAR=%ld,SW=%ld.\n",
|
|
Link->NextSend, Link->LastAckReceived, Link->SendWindowSize);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Reference the packet since it is given to the NDIS driver.
|
|
//
|
|
|
|
#if DBG
|
|
NbfReferencePacket (Packet);
|
|
#else
|
|
++Packet->ReferenceCount; // OK since it is not queued anywhere.
|
|
#endif
|
|
|
|
//
|
|
// If this is the last I-frame in the window, then indicate that we
|
|
// should checkpoint. Also checkpoint if the sender is requesting
|
|
// acknowledgement (currently on SendSessionEnd does this).
|
|
// By default, this will also be a command frame.
|
|
//
|
|
|
|
if (((SendsOutstanding+1) >= (ULONG)Link->SendWindowSize) ||
|
|
ForceAck) {
|
|
Link->SendState = SEND_STATE_CHECKPOINTING;
|
|
StopTi (Link);
|
|
DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit.
|
|
Poll = TRUE;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If we are polling, and the caller cares about it, then
|
|
// we set LinkCheckpoint, and also set up the connection to
|
|
// be waiting for resources. We do this now, before the send,
|
|
// so that even if the ack is receive right away, we will
|
|
// be in a good state. When we return LinkCheckpoint TRUE
|
|
// the caller realizes that he no longer owns the right
|
|
// to "packetize" and exits immediately.
|
|
//
|
|
// We also want to start our retransmission timer so, if this
|
|
// packet gets dropped, we will know to retransmit it. The
|
|
// exception is if LinkCheckpoint was specified, then we
|
|
// only StartT1 of we are not polling (the caller will
|
|
// ensure it is started if he exits before we poll).
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LinkCheckpoint)) {
|
|
|
|
if (Poll) {
|
|
|
|
//
|
|
// If the connection still has send state PACKETIZE,
|
|
// then change it to W_LINK. If it is something else
|
|
// (such as W_PACKET or W_ACK) then don't worry, when
|
|
// that condition clears he will repacketize and the
|
|
// link conditions will be re-examined. In all
|
|
// case we turn off the PACKETIZE flag, because when
|
|
// we return with LinkCheckpoint TRUE he will stop
|
|
// packetizing, and to close the window we turn it
|
|
// off now (before the NdisSend) rather than then.
|
|
//
|
|
|
|
ASSERT (Connection->LinkSpinLock == &Link->SpinLock);
|
|
if (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) {
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_LINK;
|
|
}
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
if (Link->Provider->MacInfo.MediumAsync) {
|
|
Packet->Link = Link;
|
|
NbfReferenceLink ("Send I-frame", Link, LREF_START_T1);
|
|
} else {
|
|
StartT1 (Link, Packet->NdisIFrameLength);
|
|
}
|
|
*LinkCheckpoint = TRUE;
|
|
|
|
} else {
|
|
|
|
StartT1 (Link, 0);
|
|
*LinkCheckpoint = FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If LinkCheckpoint is not true, then we are sending
|
|
// an I-frame other than DFM/DOL. In this case, as
|
|
// an optimization, we'll set W_LINK if a) we are
|
|
// polling b) we are IDLE (to avoid messing up other
|
|
// states such as W_ACK). This will avoid a window
|
|
// where we don't go W_LINK until after the next
|
|
// send tries to packetize and fails.
|
|
//
|
|
|
|
if (Poll) {
|
|
|
|
ASSERT (Connection->LinkSpinLock == &Link->SpinLock);
|
|
if (Connection->SendState == CONNECTION_SENDSTATE_IDLE) {
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_LINK;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This is an optimization; we know that if LinkCheckpoint
|
|
// is present than we are being called from PacketizeSend;
|
|
// in this case the Link will have the LREF_CONNECTION
|
|
// reference and the connection will have the CREF_PACKETIZE
|
|
// reference, so we don't have to reference the link
|
|
// again.
|
|
//
|
|
|
|
NbfReferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND);
|
|
|
|
|
|
//
|
|
// Start the retransmission timer.
|
|
//
|
|
|
|
if (Link->Provider->MacInfo.MediumAsync) {
|
|
if (Poll) {
|
|
Packet->Link = Link;
|
|
NbfReferenceLink ("ResendPacket", Link, LREF_START_T1);
|
|
} else {
|
|
StartT1 (Link, 0);
|
|
}
|
|
} else {
|
|
StartT1 (Link, Poll ? Packet->NdisIFrameLength : 0);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Since this I-frame contains an N(R), it is potentially ACKing some
|
|
// previously received I-frames as reverse traffic. So we stop our
|
|
// delayed acknowlegement timer.
|
|
//
|
|
|
|
StopT2 (Link);
|
|
|
|
if ((Link->Provider->MacInfo.MediumAsync) &&
|
|
(ARGUMENT_PRESENT(LinkCheckpoint)) &&
|
|
(Link->SendWindowSize >= 3) &&
|
|
(!Poll) && (SendsOutstanding == (ULONG)(Link->SendWindowSize-2))) {
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
NbfReferenceConnection ("PacketizeConnection TRUE", Connection, CREF_TEMP);
|
|
Packet->PacketizeConnection = TRUE;
|
|
|
|
} else {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Send the packet; no locks held. Note that if the send fails, we will
|
|
// NOT fail upward; we allow things to continue onward. This lets us retry
|
|
// the send multiple times before we give out; additionally, it keeps us
|
|
// from failing obscurely when sending control Iframes.
|
|
//
|
|
// NOTE: NbfNdisSend releases the link spinlock.
|
|
//
|
|
|
|
NbfNdisSend (Link, Packet);
|
|
|
|
Link->PacketsSent++;
|
|
|
|
//
|
|
// Remove the reference made above if needed.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(LinkCheckpoint)) {
|
|
NbfDereferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND);
|
|
}
|
|
|
|
return Status;
|
|
|
|
} /* SendOnePacket */
|
|
|
|
|
|
VOID
|
|
SendControlPacket(
|
|
IN PTP_LINK Link,
|
|
IN PTP_PACKET Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a connection-oriented packet by calling the Physical
|
|
Provider's Send service. While SendOnePacket is used to send an I-
|
|
frame, this routine is used to send one of the following: RR, RNR, REJ,
|
|
SABME, UA, DISC, DM, FRMR, TEST, and XID.
|
|
|
|
NOTE: This function is called with the link spinlock held,
|
|
and returns with it released. IT MUST BE CALLED AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Link - Pointer to a TP_LINK object.
|
|
|
|
Packet - Pointer to a TP_PACKET object.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT i;
|
|
PUCHAR p;
|
|
PNDIS_BUFFER ndisBuffer;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_PACKET) {
|
|
NbfPrint3 ("SendControlPacket: Entered for link %lx, packet %lx, NdisPacket %lx\n 00:",
|
|
Link, Packet, Packet->NdisPacket);
|
|
IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) {
|
|
UINT PLength, PCount;
|
|
UINT BLength;
|
|
PVOID BAddr;
|
|
p = Packet->Header;
|
|
for (i=0;i<20;i++) {
|
|
NbfPrint1 (" %2x",p[i]);
|
|
}
|
|
NbfPrint0 ("\n");
|
|
NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength);
|
|
NbfPrint3 ("Sending Control Packet: %lx, Length: %lx Pages: %lx\n",
|
|
Packet->NdisPacket, PLength, PCount);
|
|
while (ndisBuffer != NULL) {
|
|
NdisQueryBuffer (ndisBuffer, &BAddr, &BLength);
|
|
NbfPrint3 ("Sending Control Packet: Buffer %08lx Length %08lx Va %08lx\n",
|
|
ndisBuffer, BLength, BAddr);
|
|
NdisGetNextBuffer (ndisBuffer, &ndisBuffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT (Packet->PacketSent == FALSE);
|
|
|
|
NbfReferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND);
|
|
|
|
//
|
|
// Send the packet (we have the lock, NbfNdisSend released
|
|
// it.
|
|
//
|
|
|
|
NbfNdisSend (Link, Packet);
|
|
|
|
NbfDereferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND);
|
|
|
|
} /* SendControlPacket */
|
|
|
|
|
|
VOID
|
|
NbfNdisSend(
|
|
IN PTP_LINK Link,
|
|
IN PTP_PACKET Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to ensure that receive sequence numbers on
|
|
packets are numbered correctly. It is called in place of NdisSend
|
|
and after assigning the receive sequence number it locks out other
|
|
sends until the NdisSend call has returned (not necessarily completed),
|
|
insuring that the packets with increasing receive sequence numbers
|
|
are queue in the right order by the MAC.
|
|
|
|
NOTE: This routine is called with the link spinlock held,
|
|
and it returns with it released. THIS ROUTINE MUST BE CALLED
|
|
AT DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Link - Pointer to a TP_LINK object.
|
|
|
|
Packet - Pointer to a TP_PACKET object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NDIS_STATUS NdisStatus;
|
|
PLIST_ENTRY p;
|
|
PDLC_S_FRAME DlcHeader;
|
|
PNDIS_PACKET TmpNdisPacket;
|
|
ULONG result;
|
|
|
|
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
if (Link->Provider->UniProcessor) {
|
|
|
|
//
|
|
// On a uni-processor, we can send without fear of
|
|
// being interrupted by an incoming packet.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]);
|
|
|
|
if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) {
|
|
|
|
//
|
|
// It's not a U-frame, so we assign RcvSeq.
|
|
//
|
|
|
|
DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1);
|
|
|
|
}
|
|
|
|
#if DBG
|
|
NbfSendsIssued++;
|
|
#endif
|
|
|
|
INCREMENT_COUNTER (Link->Provider, PacketsSent);
|
|
|
|
#if PKT_LOG
|
|
// Log this packet in connection's sent packets' queue
|
|
NbfLogSndPacket(Link, Packet);
|
|
#endif // PKT_LOG
|
|
|
|
if (Link->Loopback) {
|
|
|
|
//
|
|
// This packet is sent to ourselves; we should loop it
|
|
// back.
|
|
//
|
|
|
|
NbfInsertInLoopbackQueue(
|
|
Link->Provider,
|
|
Packet->NdisPacket,
|
|
Link->LoopbackDestinationIndex
|
|
);
|
|
|
|
NdisStatus = NDIS_STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
if (Link->Provider->NdisBindingHandle) {
|
|
|
|
NdisSend (
|
|
&NdisStatus,
|
|
Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket);
|
|
}
|
|
else {
|
|
NdisStatus = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n",
|
|
NbfGetNdisStatus(NdisStatus));
|
|
}
|
|
|
|
switch (NdisStatus) {
|
|
|
|
case NDIS_STATUS_PENDING:
|
|
#if DBG
|
|
NbfSendsPended++;
|
|
#endif
|
|
break;
|
|
|
|
case NDIS_STATUS_SUCCESS:
|
|
#if DBG
|
|
NbfSendsCompletedInline++;
|
|
NbfSendsCompletedOk++;
|
|
#endif
|
|
NbfSendCompletionHandler (Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket,
|
|
NDIS_STATUS_SUCCESS);
|
|
break;
|
|
|
|
default:
|
|
#if DBG
|
|
NbfSendsCompletedInline++;
|
|
NbfSendsCompletedFail++;
|
|
#endif
|
|
NbfSendCompletionHandler (Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket,
|
|
NDIS_STATUS_SUCCESS);
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n",
|
|
NbfGetNdisStatus (NdisStatus));
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If there is a send in progress, then queue this packet
|
|
// and return.
|
|
//
|
|
|
|
if (Link->NdisSendsInProgress > 0) {
|
|
|
|
p = (PLIST_ENTRY)(Packet->NdisPacket->MacReserved);
|
|
InsertTailList (&Link->NdisSendQueue, p);
|
|
++Link->NdisSendsInProgress;
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// No send in progress. Set the flag to true, and fill in the
|
|
// receive sequence field in the packet (note that the RcvSeq
|
|
// field is in the same place for I- and S-frames.
|
|
//
|
|
|
|
Link->NdisSendsInProgress = 1;
|
|
|
|
while (TRUE) {
|
|
|
|
DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]);
|
|
|
|
if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) {
|
|
|
|
//
|
|
// It's not a U-frame, so we assign RcvSeq.
|
|
//
|
|
|
|
DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1);
|
|
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
#if DBG
|
|
NbfSendsIssued++;
|
|
#endif
|
|
|
|
INCREMENT_COUNTER (Link->Provider, PacketsSent);
|
|
|
|
#if PKT_LOG
|
|
// Log this packet in connection's sent packets' queue
|
|
NbfLogSndPacket(Link, Packet);
|
|
#endif // PKT_LOG
|
|
|
|
if (Link->Loopback) {
|
|
|
|
//
|
|
// This packet is sent to ourselves; we should loop it
|
|
// back.
|
|
//
|
|
|
|
NbfInsertInLoopbackQueue(
|
|
Link->Provider,
|
|
Packet->NdisPacket,
|
|
Link->LoopbackDestinationIndex
|
|
);
|
|
|
|
NdisStatus = NDIS_STATUS_PENDING;
|
|
|
|
} else {
|
|
|
|
if (Link->Provider->NdisBindingHandle) {
|
|
|
|
NdisSend (
|
|
&NdisStatus,
|
|
Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket);
|
|
}
|
|
else {
|
|
NdisStatus = STATUS_INVALID_DEVICE_STATE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Take the ref count down, which may allow others
|
|
// to come through.
|
|
//
|
|
|
|
result = ExInterlockedAddUlong(
|
|
&Link->NdisSendsInProgress,
|
|
(ULONG)-1,
|
|
&Link->SpinLock);
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n",
|
|
NbfGetNdisStatus(NdisStatus));
|
|
}
|
|
|
|
switch (NdisStatus) {
|
|
|
|
case NDIS_STATUS_PENDING:
|
|
#if DBG
|
|
NbfSendsPended++;
|
|
#endif
|
|
break;
|
|
|
|
case NDIS_STATUS_SUCCESS:
|
|
#if DBG
|
|
NbfSendsCompletedInline++;
|
|
NbfSendsCompletedOk++;
|
|
#endif
|
|
NbfSendCompletionHandler (Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket,
|
|
NDIS_STATUS_SUCCESS);
|
|
break;
|
|
|
|
default:
|
|
#if DBG
|
|
NbfSendsCompletedInline++;
|
|
NbfSendsCompletedFail++;
|
|
#endif
|
|
NbfSendCompletionHandler (Link->Provider->NdisBindingHandle,
|
|
Packet->NdisPacket,
|
|
NDIS_STATUS_SUCCESS);
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n",
|
|
NbfGetNdisStatus (NdisStatus));
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// We have now sent a packet, see if any queued up while we
|
|
// were doing it. If the count was zero after removing ours,
|
|
// then anything else queued is being processed, so we can
|
|
// exit.
|
|
//
|
|
|
|
if (result == 1) {
|
|
return;
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
p = RemoveHeadList(&Link->NdisSendQueue);
|
|
|
|
//
|
|
// If the refcount was not zero, then nobody else should
|
|
// have taken packets off since they would have been
|
|
// blocked by us. So, the queue should not be empty.
|
|
//
|
|
|
|
ASSERT (p != &Link->NdisSendQueue);
|
|
|
|
//
|
|
// Get back the TP_PACKET by using the Frame pointer in the
|
|
// ProtocolReserved field of the NDIS_PACKET.
|
|
//
|
|
|
|
TmpNdisPacket = CONTAINING_RECORD (p, NDIS_PACKET, MacReserved[0]);
|
|
Packet = (PTP_PACKET)(((PSEND_PACKET_TAG)(&TmpNdisPacket->ProtocolReserved[0]))->Frame);
|
|
|
|
} // while loop
|
|
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
}
|
|
|
|
} /* NbfNdisSend */
|
|
|
|
|
|
VOID
|
|
RestartLinkTraffic(
|
|
PTP_LINK Link
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine continues the activities of the connections on a link.
|
|
|
|
NOTE: This function is called with the link spinlock held and
|
|
it returns with it released. THIS FUNCTION MUST BE CALLED AT
|
|
DPC LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Link - Pointer to a TP_LINK object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTP_CONNECTION connection;
|
|
PLIST_ENTRY p;
|
|
|
|
IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
NbfPrint1 ("RestartLinkTraffic: Entered for link %lx.\n", Link);
|
|
}
|
|
|
|
//
|
|
// Link conditions may have cleared up. Make all connections on this
|
|
// link eligible for more packetization if they are in W_LINK state.
|
|
//
|
|
|
|
for (p = Link->ConnectionDatabase.Flink;
|
|
p != &Link->ConnectionDatabase;
|
|
p = p->Flink) {
|
|
|
|
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
|
|
|
ASSERT (connection->LinkSpinLock == &Link->SpinLock);
|
|
|
|
//
|
|
// If we tried to send a plain-ole data frame DFM/DOL, but
|
|
// link conditions were not satisfactory, then we changed
|
|
// send state to W_LINK. Check for that now, and possibly
|
|
// start repacketizing.
|
|
//
|
|
|
|
if (connection->SendState == CONNECTION_SENDSTATE_W_LINK) {
|
|
if (!(IsListEmpty (&connection->SendQueue))) {
|
|
|
|
connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
|
|
|
//
|
|
// This is similar to calling StartPacketizingConnection
|
|
// with the Immediate set to FALSE.
|
|
//
|
|
|
|
if (!(connection->Flags & CONNECTION_FLAGS_PACKETIZE) &&
|
|
(connection->Flags & CONNECTION_FLAGS_READY)) {
|
|
|
|
ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING));
|
|
connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE);
|
|
|
|
ExInterlockedInsertTailList(
|
|
&connection->Provider->PacketizeQueue,
|
|
&connection->PacketizeLinkage,
|
|
&connection->Provider->SpinLock);
|
|
|
|
}
|
|
|
|
} else {
|
|
connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
|
|
|
} /* RestartLinkTraffic */
|
|
|
|
|
|
VOID
|
|
NbfProcessWanDelayedQueue(
|
|
IN PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the thread routine which restarts packetizing
|
|
that has been delayed on WAN to allow RRs to come in.
|
|
This is very similar to PacketizeConnections.
|
|
|
|
Arguments:
|
|
|
|
Parameter - A pointer to the device context.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PLIST_ENTRY p;
|
|
PTP_CONNECTION Connection;
|
|
KIRQL oldirql;
|
|
|
|
DeviceContext = (PDEVICE_CONTEXT)Parameter;
|
|
|
|
//
|
|
// Packetize all waiting connections
|
|
//
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &oldirql);
|
|
ASSERT (DeviceContext->WanThreadQueued);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
|
|
while (!IsListEmpty(&DeviceContext->PacketizeQueue)) {
|
|
|
|
p = RemoveHeadList(&DeviceContext->PacketizeQueue);
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage);
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) {
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
|
NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE);
|
|
} else {
|
|
NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET);
|
|
PacketizeSend (Connection, FALSE); // releases the lock.
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
|
|
}
|
|
|
|
DeviceContext->WanThreadQueued = FALSE;
|
|
|
|
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
|
|
|
KeLowerIrql (oldirql);
|
|
|
|
} /* NbfProcessWanDelayedQueue */
|
|
|
|
|
|
NTSTATUS
|
|
BuildBufferChainFromMdlChain (
|
|
IN PDEVICE_CONTEXT DeviceContext,
|
|
IN PMDL CurrentMdl,
|
|
IN ULONG ByteOffset,
|
|
IN ULONG DesiredLength,
|
|
OUT PNDIS_BUFFER *Destination,
|
|
OUT PMDL *NewCurrentMdl,
|
|
OUT ULONG *NewByteOffset,
|
|
OUT ULONG *TrueLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to build an NDIS_BUFFER chain from a source Mdl chain and
|
|
offset into it. We assume we don't know the length of the source Mdl chain,
|
|
and we must allocate the NDIS_BUFFERs for the destination chain, which
|
|
we do from the NDIS buffer pool.
|
|
|
|
The NDIS_BUFFERs that are returned are mapped and locked. (Actually, the pages in
|
|
them are in the same state as those in the source MDLs.)
|
|
|
|
If the system runs out of memory while we are building the destination
|
|
NDIS_BUFFER chain, we completely clean up the built chain and return with
|
|
NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl
|
|
and ByteOffset. TrueLength is set to 0.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode, Source Mdls locked. It is recommended, although not required,
|
|
that the source Mdls be mapped and locked prior to calling this routine.
|
|
|
|
Arguments:
|
|
|
|
BufferPoolHandle - The buffer pool to allocate buffers from.
|
|
|
|
CurrentMdl - Points to the start of the Mdl chain from which to draw the
|
|
packet.
|
|
|
|
ByteOffset - Offset within this MDL to start the packet at.
|
|
|
|
DesiredLength - The number of bytes to insert into the packet.
|
|
|
|
Destination - returned pointer to the NDIS_BUFFER chain describing the packet.
|
|
|
|
NewCurrentMdl - returned pointer to the Mdl that would be used for the next
|
|
byte of packet. NULL if the source Mdl chain was exhausted.
|
|
|
|
NewByteOffset - returned offset into the NewCurrentMdl for the next byte of
|
|
packet. NULL if the source Mdl chain was exhausted.
|
|
|
|
TrueLength - The actual length of the returned NDIS_BUFFER Chain. If less than
|
|
DesiredLength, the source Mdl chain was exhausted.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded (even if
|
|
shorter than the desired chain).
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while building the
|
|
destination chain.
|
|
|
|
--*/
|
|
{
|
|
ULONG AvailableBytes;
|
|
PMDL OldMdl;
|
|
PNDIS_BUFFER NewNdisBuffer;
|
|
NDIS_STATUS NdisStatus;
|
|
|
|
//
|
|
|
|
IF_NBFDBG (NBF_DEBUG_NDIS) {
|
|
NbfPrint3 ("BuildBufferChain: Mdl: %lx Offset: %ld Length: %ld\n",
|
|
CurrentMdl, ByteOffset, DesiredLength);
|
|
}
|
|
|
|
AvailableBytes = MmGetMdlByteCount (CurrentMdl) - ByteOffset;
|
|
if (AvailableBytes > DesiredLength) {
|
|
AvailableBytes = DesiredLength;
|
|
}
|
|
|
|
OldMdl = CurrentMdl;
|
|
*NewCurrentMdl = OldMdl;
|
|
*NewByteOffset = ByteOffset + AvailableBytes;
|
|
*TrueLength = AvailableBytes;
|
|
|
|
|
|
//
|
|
// Build the first NDIS_BUFFER, which could conceivably be the only one...
|
|
//
|
|
|
|
NdisCopyBuffer(
|
|
&NdisStatus,
|
|
&NewNdisBuffer,
|
|
DeviceContext->NdisBufferPool,
|
|
OldMdl,
|
|
ByteOffset,
|
|
AvailableBytes);
|
|
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
*NewByteOffset = ByteOffset;
|
|
*TrueLength = 0;
|
|
*Destination = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*Destination = NewNdisBuffer;
|
|
|
|
|
|
// IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
// PVOID PAddr, UINT PLen;
|
|
// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen);
|
|
// NbfPrint4 ("BuildBufferChain: (start)Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n",
|
|
// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr);
|
|
// }
|
|
|
|
//
|
|
// Was the first NDIS_BUFFER enough data, or are we out of Mdls?
|
|
//
|
|
|
|
if ((AvailableBytes == DesiredLength) || (OldMdl->Next == NULL)) {
|
|
if (*NewByteOffset >= MmGetMdlByteCount (OldMdl)) {
|
|
*NewCurrentMdl = OldMdl->Next;
|
|
*NewByteOffset = 0;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Need more data, so follow the in Mdl chain to create a packet.
|
|
//
|
|
|
|
OldMdl = OldMdl->Next;
|
|
*NewCurrentMdl = OldMdl;
|
|
|
|
while (OldMdl != NULL) {
|
|
AvailableBytes = DesiredLength - *TrueLength;
|
|
if (AvailableBytes > MmGetMdlByteCount (OldMdl)) {
|
|
AvailableBytes = MmGetMdlByteCount (OldMdl);
|
|
}
|
|
|
|
NdisCopyBuffer(
|
|
&NdisStatus,
|
|
&(NDIS_BUFFER_LINKAGE(NewNdisBuffer)),
|
|
DeviceContext->NdisBufferPool,
|
|
OldMdl,
|
|
0,
|
|
AvailableBytes);
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// ran out of resources. put back what we've used in this call and
|
|
// return the error.
|
|
//
|
|
|
|
while (*Destination != NULL) {
|
|
NewNdisBuffer = NDIS_BUFFER_LINKAGE(*Destination);
|
|
NdisFreeBuffer (*Destination);
|
|
*Destination = NewNdisBuffer;
|
|
}
|
|
|
|
*NewByteOffset = ByteOffset;
|
|
*TrueLength = 0;
|
|
*NewCurrentMdl = CurrentMdl;
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer);
|
|
|
|
*TrueLength += AvailableBytes;
|
|
*NewByteOffset = AvailableBytes;
|
|
|
|
// IF_NBFDBG (NBF_DEBUG_SENDENG) {
|
|
// PVOID PAddr, UINT PLen;
|
|
// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen);
|
|
// NbfPrint4 ("BuildBufferChain: (continue) Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n",
|
|
// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr);
|
|
// }
|
|
|
|
if (*TrueLength == DesiredLength) {
|
|
if (*NewByteOffset == MmGetMdlByteCount (OldMdl)) {
|
|
*NewCurrentMdl = OldMdl->Next;
|
|
*NewByteOffset = 0;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
OldMdl = OldMdl->Next;
|
|
*NewCurrentMdl = OldMdl;
|
|
|
|
} // while (mdl chain exists)
|
|
|
|
*NewCurrentMdl = NULL;
|
|
*NewByteOffset = 0;
|
|
return STATUS_SUCCESS;
|
|
|
|
} // BuildBufferChainFromMdlChain
|
|
|