Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2888 lines
86 KiB

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
send.c
Abstract:
This module contains the send routines for the Netbios
module of the ISN transport.
Author:
Adam Barr (adamba) 22-November-1993
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
//
// Work Item structure for work items put on the Kernel Excutive worker threads
//
typedef struct
{
WORK_QUEUE_ITEM Item; // Used by OS to queue these requests
PVOID Context;
} NBI_WORK_ITEM_CONTEXT;
VOID
SendDgram(
PNDIS_PACKET Packet
)
/*++
Routine Description:
This routine sends a datagram from a Worker thread.
Earlier, this code was part of the NbiSendComplete module,
but since we could end up with a stack overflow, this may
now be handled over a worker thread.
Arguments:
WorkItem - The work item that was allocated for this.
Return Value:
None.
--*/
{
PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
NDIS_STATUS Status;
PNETBIOS_CACHE CacheName;
PDEVICE Device = NbiDevice;
NB_CONNECTIONLESS UNALIGNED * Header;
PIPX_LOCAL_TARGET LocalTarget;
ULONG HeaderLength;
ULONG PacketLength;
// send the datagram on the next net.
CTEAssert (!Reserved->u.SR_DG.Cache->Unique);
Reserved->SendInProgress = TRUE;
CacheName = Reserved->u.SR_DG.Cache;
//
// Fill in the IPX header -- the default header has the broadcast
// address on net 0 as the destination IPX address, so we modify
// that for the current netbios cache entry if needed.
//
Header = (NB_CONNECTIONLESS UNALIGNED *) (&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER));
*(UNALIGNED ULONG *)Header->IpxHeader.DestinationNetwork = CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].Network;
RtlCopyMemory (&Header->IpxHeader.DestinationNode, BroadcastAddress, 6);
LocalTarget = &CacheName->Networks[Reserved->u.SR_DG.CurrentNetwork].LocalTarget;
HeaderLength = sizeof(IPX_HEADER) + sizeof(NB_DATAGRAM);
PacketLength = HeaderLength + (ULONG) REQUEST_INFORMATION(Reserved->u.SR_DG.DatagramRequest);
Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256);
Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256);
Header->IpxHeader.PacketType = 0x04;
//
// Now fill in the Netbios header.
//
Header->Datagram.ConnectionControlFlag = 0x00;
RtlCopyMemory(
Header->Datagram.SourceName,
Reserved->u.SR_DG.AddressFile->Address->NetbiosAddress.NetbiosName,
16);
if (Reserved->u.SR_DG.RemoteName != (PVOID)-1) {
//
// This is a directed, as opposed to broadcast, datagram.
//
Header->Datagram.DataStreamType = NB_CMD_DATAGRAM;
RtlCopyMemory(
Header->Datagram.DestinationName,
Reserved->u.SR_DG.RemoteName->NetbiosName,
16);
} else {
Header->Datagram.DataStreamType = NB_CMD_BROADCAST_DATAGRAM;
RtlZeroMemory(
Header->Datagram.DestinationName,
16);
}
//
// Now send the frame (IPX will adjust the length of the
// first buffer and the whole frame correctly).
//
if ((Status = (*Device->Bind.SendHandler) (LocalTarget,
Packet,
PacketLength,
HeaderLength)) != STATUS_PENDING) {
NbiSendComplete (Packet, Status);
}
}
VOID
NbiDelayedSendDatagram(
IN PVOID pContextInfo
)
{
NBI_WORK_ITEM_CONTEXT *pContext = (NBI_WORK_ITEM_CONTEXT *) pContextInfo;
PNDIS_PACKET Packet = (PNDIS_PACKET) pContext->Context;
PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
Reserved->CurrentSendIteration = 0;
SendDgram (Packet);
NbiFreeMemory (pContextInfo, sizeof(NBI_WORK_ITEM_CONTEXT), MEMORY_WORK_ITEM,
"Free delayed DgramSend work item");
}
VOID
NbiSendComplete(
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
)
/*++
Routine Description:
This routine handles a send completion call from IPX.
Arguments:
Packet - The packet which has been completed.
Status - The status of the send.
Return Value:
None.
--*/
{
PDEVICE Device = NbiDevice;
PADDRESS Address;
PADDRESS_FILE AddressFile;
PCONNECTION Connection;
PREQUEST DatagramRequest;
PREQUEST SendRequest, TmpRequest;
PNDIS_BUFFER CurBuffer, TmpBuffer;
PNETBIOS_CACHE CacheName;
PNDIS_BUFFER SecondBuffer = NULL;
PVOID SecondBufferMemory = NULL;
UINT SecondBufferLength;
ULONG oldvalue;
PNB_SEND_RESERVED Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
CTELockHandle CancelLH;
#if defined(_PNP_POWER)
CTELockHandle LockHandle;
#endif _PNP_POWER
//
// We jump back here if we re-call send from inside this
// function and it doesn't pend (to avoid stack overflow).
//
++Device->Statistics.PacketsSent;
switch (Reserved->Type) {
case SEND_TYPE_SESSION_DATA:
//
// This was a send on a session. This references the
// IRP.
//
NB_DEBUG2 (SEND, ("Complete NDIS packet %lx\n", Reserved));
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
Connection = Reserved->u.SR_CO.Connection;
SendRequest = Reserved->u.SR_CO.Request;
if (!Reserved->u.SR_CO.NoNdisBuffer) {
CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer));
while (CurBuffer) {
TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer);
NdisFreeBuffer (CurBuffer);
CurBuffer = TmpBuffer;
}
}
//
// If NoNdisBuffer is TRUE, then we could set
// Connection->SendBufferInUse to FALSE here. The
// problem is that a new send might be in progress
// by the time this completes and it may have
// used the user buffer, then if we need to
// retransmit that packet we would use the buffer
// twice. We instead rely on the fact that whenever
// we make a new send active we set SendBufferInUse
// to FALSE. The net effect is that the user's buffer
// can be used the first time a send is packetize
// but not on resends.
//
NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL;
NdisRecalculatePacketCounts (Packet);
#if DBG
if (REQUEST_REFCOUNT(SendRequest) > 100) {
DbgPrint ("Request %lx (%lx) has high refcount\n",
Connection, SendRequest);
DbgBreakPoint();
}
#endif
#if defined(__PNP)
NB_GET_LOCK( &Connection->Lock, &LockHandle );
oldvalue = REQUEST_REFCOUNT(SendRequest)--;
if ( DEVICE_NETWORK_PATH_NOT_FOUND == Status ) {
Connection->LocalTarget = Reserved->LocalTarget;
}
NB_FREE_LOCK( &Connection->Lock, LockHandle );
#else
oldvalue = NB_ADD_ULONG(
&REQUEST_REFCOUNT (SendRequest),
(ULONG)-1,
&Connection->Lock);
#endif __PNP
if (oldvalue == 1) {
//
// If the refcount on this request is now zero then
// we already got the ack for it, which means
// that the ack-processing code has unlinked the
// request from Connection->SendQueue. So we
// can just run the queue of connections here
// and complete them.
//
// We dereference the connection for all but one
// of the requests, we hang on to that until a bit
// later so everything stays around.
//
while (TRUE) {
TmpRequest = REQUEST_SINGLE_LINKAGE (SendRequest);
NB_DEBUG2 (SEND, ("Completing request %lx from send complete\n", SendRequest));
REQUEST_STATUS (SendRequest) = STATUS_SUCCESS;
NB_GET_CANCEL_LOCK( &CancelLH );
IoSetCancelRoutine (SendRequest, (PDRIVER_CANCEL)NULL);
NB_FREE_CANCEL_LOCK( CancelLH );
NbiCompleteRequest (SendRequest);
NbiFreeRequest (Device, SendRequest);
++Connection->ConnectionInfo.TransmittedTsdus;
SendRequest = TmpRequest;
if (SendRequest == NULL) {
break;
}
NbiDereferenceConnection (Connection, CREF_SEND);
}
}
if (Reserved->OwnedByConnection) {
Connection->SendPacketInUse = FALSE;
if (Connection->OnWaitPacketQueue) {
//
// This will put the connection on the packetize
// queue if appropriate.
//
NbiCheckForWaitPacket (Connection);
}
} else {
NbiPushSendPacket(Reserved);
}
if (oldvalue == 1) {
NbiDereferenceConnection (Connection, CREF_SEND);
}
break;
case SEND_TYPE_NAME_FRAME:
//
// The frame is an add name/delete name; put it back in
// the pool and deref the address.
//
CTEAssert (Reserved->SendInProgress);
Address = Reserved->u.SR_NF.Address;
Reserved->SendInProgress = FALSE;
NbiPushSendPacket (Reserved);
if (Address) {
NbiDereferenceAddress (Address, AREF_NAME_FRAME);
} else {
NbiDereferenceDevice (Device, DREF_NAME_FRAME);
}
break;
case SEND_TYPE_SESSION_INIT:
//
// This is a session initialize or session init ack; free
// the second buffer, put the packet back in the pool and
// deref the device.
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
NdisUnchainBufferAtBack (Packet, &SecondBuffer);
if (SecondBuffer)
{
NdisQueryBufferSafe (SecondBuffer, &SecondBufferMemory, &SecondBufferLength, HighPagePriority);
CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT));
if (SecondBufferMemory)
{
NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION,
"Session Initialize");
}
NdisFreeBuffer(SecondBuffer);
}
NbiPushSendPacket (Reserved);
NbiDereferenceDevice (Device, DREF_SESSION_INIT);
break;
case SEND_TYPE_SESSION_NO_DATA:
//
// This is a frame which was sent on a connection but
// has no data (ack, session end, session end ack).
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
Connection = Reserved->u.SR_CO.Connection;
if (Reserved->OwnedByConnection) {
CTEAssert (Connection != NULL);
Connection->SendPacketInUse = FALSE;
if (Connection->OnWaitPacketQueue) {
//
// This will put the connection on the packetize
// queue if appropriate.
//
NbiCheckForWaitPacket (Connection);
}
} else {
NbiPushSendPacket(Reserved);
}
if (Connection != NULL) {
NbiDereferenceConnection (Connection, CREF_FRAME);
} else {
NbiDereferenceDevice (Device, DREF_FRAME);
}
break;
case SEND_TYPE_FIND_NAME:
//
// The frame is a find name; just set SendInProgress to
// FALSE and FindNameTimeout will clean it up.
//
#if defined(_PNP_POWER)
NB_GET_LOCK( &Device->Lock, &LockHandle);
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
//
// We keep track of when it finds a net that isn't
// a down wan line so that we can tell when datagram
// sends should fail (otherwise we succeed them, so
// the browser won't think this is a down wan line).
//
if ( STATUS_SUCCESS == Status ) {
NB_SET_SR_FN_SENT_ON_UP_LINE (Reserved, TRUE);
} else {
NB_DEBUG( CACHE, ("Send complete of find name with failure %lx\n",Status ));
}
NB_FREE_LOCK(&Device->Lock, LockHandle);
#else
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
#endif _PNP_POWER
break;
case SEND_TYPE_DATAGRAM:
//
// If there are any more networks to send this on then
// do so, otherwise put it back in the pool and complete
// the request.
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
if ((Reserved->u.SR_DG.Cache == NULL) ||
(++Reserved->u.SR_DG.CurrentNetwork >=
Reserved->u.SR_DG.Cache->NetworksUsed)) {
AddressFile = Reserved->u.SR_DG.AddressFile;
DatagramRequest = Reserved->u.SR_DG.DatagramRequest;
NB_DEBUG2 (DATAGRAM, ("Completing datagram %lx on %lx\n", DatagramRequest, AddressFile));
//
// Remove any user buffers chained on this packet.
//
NdisReinitializePacket (Packet);
NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL;
NdisChainBufferAtFront (Packet, Reserved->HeaderBuffer);
//
// Complete the request.
//
REQUEST_STATUS(DatagramRequest) = Status;
NbiCompleteRequest(DatagramRequest);
NbiFreeRequest (Device, DatagramRequest);
CacheName = Reserved->u.SR_DG.Cache;
NbiPushSendPacket (Reserved);
//
// Since we are no longer referencing the cache
// name, see if we should delete it (this will
// happen if the cache entry was aged out while
// the datagram was being processed).
//
if (CacheName != NULL) {
oldvalue = NB_ADD_ULONG(
&CacheName->ReferenceCount,
(ULONG)-1,
&Device->Lock);
if (oldvalue == 1) {
NB_DEBUG2 (CACHE, ("Free aged cache entry %lx\n", CacheName));
NbiFreeMemory(
CacheName,
sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
MEMORY_CACHE,
"Free old cache");
}
}
NbiDereferenceAddressFile (AddressFile, AFREF_SEND_DGRAM);
} else {
NBI_WORK_ITEM_CONTEXT *WorkItem;
if ((++Reserved->CurrentSendIteration >= MAX_SEND_ITERATIONS) &&
(WorkItem = (NBI_WORK_ITEM_CONTEXT *) NbiAllocateMemory (sizeof(NBI_WORK_ITEM_CONTEXT),
MEMORY_WORK_ITEM,
"Delayed DgramSend work item")))
{
WorkItem->Context = (PVOID) Packet;
ExInitializeWorkItem (&WorkItem->Item, NbiDelayedSendDatagram, (PVOID)WorkItem);
ExQueueWorkItem(&WorkItem->Item, DelayedWorkQueue);
}
else
{
SendDgram (Packet);
}
}
break;
case SEND_TYPE_STATUS_QUERY:
//
// This is an adapter status query, which is a simple
// packet.
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
NbiPushSendPacket (Reserved);
NbiDereferenceDevice (Device, DREF_STATUS_FRAME);
break;
case SEND_TYPE_STATUS_RESPONSE:
//
// This is an adapter status response, we have to free the
// second buffer.
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
NdisUnchainBufferAtBack (Packet, &SecondBuffer);
if (SecondBuffer)
{
NdisQueryBufferSafe (SecondBuffer, &SecondBufferMemory, &SecondBufferLength, HighPagePriority);
if (SecondBufferMemory)
{
NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS,
"Adapter Status");
}
NdisFreeBuffer(SecondBuffer);
}
NbiPushSendPacket (Reserved);
NbiDereferenceDevice (Device, DREF_STATUS_RESPONSE);
break;
#ifdef RSRC_TIMEOUT_DBG
case SEND_TYPE_DEATH_PACKET:
//
// This is a session initialize or session init ack; free
// the second buffer, put the packet back in the pool and
// deref the device.
//
CTEAssert (Reserved->SendInProgress);
Reserved->SendInProgress = FALSE;
DbgPrint("********Death packet send completed status %lx\n",Status);
DbgBreakPoint();
break;
#endif //RSRC_TIMEOUT_DBG
default:
CTEAssert (FALSE);
break;
}
} /* NbiSendComplete */
#if 0
ULONG NbiLoudSendQueue = 1;
#endif
VOID
NbiAssignSequenceAndSend(
IN PCONNECTION Connection,
IN PNDIS_PACKET Packet
IN NB_LOCK_HANDLE_PARAM(LockHandle)
)
/*++
Routine Description:
This routine is used to ensure that receive sequence numbers on
packets are numbered correctly. It is called in place of the lower-level
send handler; 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 CONNECTION LOCK HELD, AND
RETURNS WITH IT RELEASED.
Arguments:
Connection - The connection the send is on.
Packet - The packet to send.
LockHandle - The handle with which Connection->Lock was acquired.
Return Value:
None.
--*/
{
NDIS_STATUS NdisStatus;
PNB_SEND_RESERVED Reserved;
PLIST_ENTRY p;
NB_CONNECTION UNALIGNED * Header;
PDEVICE Device = NbiDevice;
BOOLEAN NdisSendReference;
ULONG result;
Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
CTEAssert (Connection->State == CONNECTION_STATE_ACTIVE);
//
// If there is a send in progress, then queue this packet
// and return.
//
if (Connection->NdisSendsInProgress > 0) {
NB_DEBUG2 (SEND, ("Queueing send packet %lx on %lx\n", Reserved, Connection));
InsertTailList (&Connection->NdisSendQueue, &Reserved->WaitLinkage);
++Connection->NdisSendsInProgress;
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
return;
}
//
// No send in progress. Set the flag to true, and fill in the
// receive sequence fields in the packet.
//
Connection->NdisSendsInProgress = 1;
NdisSendReference = FALSE;
Connection->NdisSendReference = &NdisSendReference;
while (TRUE) {
Header = (NB_CONNECTION UNALIGNED *)
(&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
Header->Session.ReceiveSequence = Connection->ReceiveSequence;
if (Connection->NewNetbios) {
Header->Session.ReceiveSequenceMax = Connection->LocalRcvSequenceMax;
} else {
Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset;
}
//
// Since we are acking as much as we know, we can clear
// this flag. The connection will eventually get removed
// from the queue by the long timeout.
//
Connection->DataAckPending = FALSE;
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(NB_CONNECTION));
NdisStatus = (*Device->Bind.SendHandler)(
&Connection->LocalTarget,
Packet,
Reserved->u.SR_CO.PacketLength,
sizeof(NB_CONNECTION));
if (NdisStatus != NDIS_STATUS_PENDING) {
NbiSendComplete(
Packet,
NdisStatus);
}
//
// Take the ref count down, which may allow others
// to come through.
//
result = NB_ADD_ULONG(
&Connection->NdisSendsInProgress,
(ULONG)-1,
&Connection->Lock);
//
// 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 the connection was stopped while we were sending,
// a special reference was added which we remove (NbiStopConnection
// sets NdisSendReference to TRUE, using the pointer saved
// in Connection->NdisSendReference).
//
if (result == 1) {
if (NdisSendReference) {
NB_DEBUG2 (SEND, ("Remove CREF_NDIS_SEND from %lx\n", Connection));
NbiDereferenceConnection (Connection, CREF_NDIS_SEND);
}
return;
}
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
p = RemoveHeadList(&Connection->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 != &Connection->NdisSendQueue);
Reserved = CONTAINING_RECORD (p, NB_SEND_RESERVED, WaitLinkage);
Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
} // while loop
//
// We should never reach here.
//
CTEAssert (FALSE);
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
} /* NbiAssignSequenceAndSend */
NTSTATUS
NbiTdiSend(
IN PDEVICE Device,
IN PREQUEST Request
)
/*++
Routine Description:
This routine does a send on an active connection.
Arguments:
Device - The netbios device.
Request - The request describing the send.
Return Value:
NTSTATUS - status of operation.
--*/
{
PCONNECTION Connection;
PTDI_REQUEST_KERNEL_SEND Parameters;
NB_DEFINE_SYNC_CONTEXT (SyncContext)
NB_DEFINE_LOCK_HANDLE (LockHandle)
CTELockHandle CancelLH;
//
// Check that the file type is valid
//
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
{
CTEAssert(FALSE);
return (STATUS_INVALID_ADDRESS_COMPONENT);
}
//
// First make sure the connection is valid.
//
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
if (Connection->Type == NB_CONNECTION_SIGNATURE) {
NB_GET_CANCEL_LOCK( &CancelLH );
NB_BEGIN_SYNC (&SyncContext);
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
//
// Make sure the connection is in a good state.
//
if (Connection->State == CONNECTION_STATE_ACTIVE) {
//
// If the connection is idle then send it now, otherwise
// queue it.
//
if (!Request->Cancel) {
Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request);
//
// For old netbios, don't allow sends greater than 64K-1.
//
if ((Connection->NewNetbios) ||
(Parameters->SendLength <= 0xffff)) {
IoSetCancelRoutine (Request, NbiCancelSend);
NB_SYNC_SWAP_IRQL( CancelLH, LockHandle );
NB_FREE_CANCEL_LOCK( CancelLH );
REQUEST_INFORMATION (Request) = Parameters->SendLength; // assume it succeeds.
REQUEST_REFCOUNT (Request) = 1; // refcount starts at 1.
NbiReferenceConnectionSync (Connection, CREF_SEND);
//
// NOTE: The connection send queue is managed such
// that the current send being packetized is not on
// the queue. For multiple-request messages, the
// first one is not on the queue, but its linkage
// field points to the next request in the message
// (which will be on the head of the queue).
//
if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) {
//
// This is a final send.
//
if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) {
NB_DEBUG2 (SEND, ("Send %lx, connection %lx idle\n", Request, Connection));
Connection->CurrentSend.Request = Request;
Connection->CurrentSend.MessageOffset = 0;
Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request);
Connection->CurrentSend.BufferOffset = 0;
Connection->SendBufferInUse = FALSE;
Connection->UnAckedSend = Connection->CurrentSend;
Connection->FirstMessageRequest = Request;
#ifdef RSRC_TIMEOUT_DBG
KeQuerySystemTime(&Connection->FirstMessageRequestTime);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Connection->FirstMessageRequestTime.QuadPart;
#endif //RSRC_TIMEOUT_DBG
Connection->LastMessageRequest = Request;
Connection->CurrentMessageLength = Parameters->SendLength;
//
// This frees the connection lock.
//
NbiPacketizeSend(
Connection
NB_LOCK_HANDLE_ARG(LockHandle)
);
} else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) {
//
// We have been collecting partial sends waiting
// for a final one, which we have now received,
// so start packetizing.
//
// We chain it on the back of the send queue,
// in addition if this is the second request in the
// message, we have to link the first request (which
// is not on the queue) to this one.
//
//
NB_DEBUG2 (SEND, ("Send %lx, connection %lx got eor\n", Request, Connection));
Connection->LastMessageRequest = Request;
Connection->CurrentMessageLength += Parameters->SendLength;
if (Connection->SendQueue.Head == NULL) {
REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request;
}
REQUEST_SINGLE_LINKAGE(Request) = NULL;
REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request);
Connection->UnAckedSend = Connection->CurrentSend;
#ifdef RSRC_TIMEOUT_DBG
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Time.QuadPart;
}
#endif //RSRC_TIMEOUT_DBG
//
// This frees the connection lock.
//
NbiPacketizeSend(
Connection
NB_LOCK_HANDLE_ARG(LockHandle)
);
} else {
//
// The state is PACKETIZE, W_ACK, or W_PACKET.
//
NB_DEBUG2 (SEND, ("Send %lx, connection %lx busy\n", Request, Connection));
REQUEST_SINGLE_LINKAGE(Request) = NULL;
REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request);
#ifdef RSRC_TIMEOUT_DBG
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Time.QuadPart;
}
#endif //RSRC_TIMEOUT_DBG
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} else {
//
// This is a partial send. We queue them up without
// packetizing until we get a final (this is because
// we have to put a correct Connection->CurrentMessageLength
// in the frames.
//
if (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE) {
//
// Start collecting partial sends. NOTE: Partial sends
// are always inserted in the send queue
//
Connection->CurrentSend.Request = Request;
Connection->CurrentSend.MessageOffset = 0;
Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request);
Connection->CurrentSend.BufferOffset = 0;
Connection->SendBufferInUse = FALSE;
Connection->FirstMessageRequest = Request;
#ifdef RSRC_TIMEOUT_DBG
KeQuerySystemTime(&Connection->FirstMessageRequestTime);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Connection->FirstMessageRequestTime.QuadPart;
#endif //RSRC_TIMEOUT_DBG
Connection->CurrentMessageLength = Parameters->SendLength;
Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR;
} else if (Connection->SubState == CONNECTION_SUBSTATE_A_W_EOR) {
//
// We have got another partial send to add to our
// list. We chain it on the back of the send queue,
// in addition if this is the second request in the
// message, we have to link the first request (which
// is not on the queue) to this one.
//
Connection->LastMessageRequest = Request;
Connection->CurrentMessageLength += Parameters->SendLength;
if (Connection->SendQueue.Head == NULL) {
REQUEST_SINGLE_LINKAGE(Connection->FirstMessageRequest) = Request;
}
REQUEST_SINGLE_LINKAGE(Request) = NULL;
REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request);
#ifdef RSRC_TIMEOUT_DBG
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Time.QuadPart;
}
#endif //RSRC_TIMEOUT_DBG
} else {
REQUEST_SINGLE_LINKAGE(Request) = NULL;
REQUEST_LIST_INSERT_TAIL(&Connection->SendQueue, Request);
#ifdef RSRC_TIMEOUT_DBG
{
LARGE_INTEGER Time;
KeQuerySystemTime(&Time);
(((LARGE_INTEGER UNALIGNED *)&(IoGetCurrentIrpStackLocation(Request))->Parameters.Others.Argument3))->QuadPart =
Time.QuadPart;
}
#endif //RSRC_TIMEOUT_DBG
}
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
NB_END_SYNC (&SyncContext);
return STATUS_PENDING;
} else {
NB_DEBUG2 (SEND, ("Send %lx, too long for connection %lx (%d)\n", Request, Connection, Parameters->SendLength));
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
NB_END_SYNC (&SyncContext);
NB_FREE_CANCEL_LOCK( CancelLH );
return STATUS_INVALID_PARAMETER;
}
} else {
NB_DEBUG2 (SEND, ("Send %lx, connection %lx cancelled\n", Request, Connection));
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
NB_END_SYNC (&SyncContext);
NB_FREE_CANCEL_LOCK( CancelLH );
return STATUS_CANCELLED;
}
} else {
NB_DEBUG (SEND, ("Send connection %lx state is %d\n", Connection, Connection->State));
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
NB_END_SYNC (&SyncContext);
NB_FREE_CANCEL_LOCK( CancelLH );
return STATUS_INVALID_CONNECTION;
}
} else {
NB_DEBUG (SEND, ("Send connection %lx has bad signature\n", Connection));
return STATUS_INVALID_CONNECTION;
}
} /* NbiTdiSend */
VOID
NbiPacketizeSend(
IN PCONNECTION Connection
IN NB_LOCK_HANDLE_PARAM(LockHandle)
)
/*++
Routine Description:
This routine does a send on an active connection.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT RELEASED.
Arguments:
Connection - The connection.
LockHandle - The handle used to acquire the lock.
Return Value:
None.
--*/
{
PREQUEST Request;
PNDIS_PACKET Packet;
PNDIS_BUFFER BufferChain;
PNB_SEND_RESERVED Reserved;
PDEVICE Device = NbiDevice;
NB_CONNECTION UNALIGNED * Header;
ULONG PacketLength;
ULONG PacketSize;
ULONG DesiredLength;
ULONG ActualLength;
NTSTATUS Status;
PSINGLE_LIST_ENTRY s;
USHORT ThisSendSequence;
USHORT ThisOffset;
BOOLEAN ExitAfterSend;
UCHAR ConnectionControlFlag;
CTELockHandle DeviceLockHandle;
//
// We jump back here if we are talking new Netbios and it
// is OK to packetize another send.
//
SendAnotherPacket:
//
// If we decide to packetize another send after this, we
// change ExitAfterSend to FALSE and SubState to PACKETIZE.
// Right now we don't change SubState in case it is W_PACKET.
//
ExitAfterSend = TRUE;
CTEAssert (Connection->CurrentSend.Request != NULL);
if (Connection->NewNetbios) {
//
// Check that we have send window, both that advertised
// by the remote and our own locally-decided window which
// may be smaller.
//
if (((USHORT)(Connection->CurrentSend.SendSequence-1) == Connection->RemoteRcvSequenceMax) ||
(((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize)) {
//
// Keep track of whether we are waiting because of his window
// or because of our local window. If it is because of our local
// window then we may want to adjust it after this window
// is acked.
//
if ((USHORT)(Connection->CurrentSend.SendSequence-1) != Connection->RemoteRcvSequenceMax) {
Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK;
NB_DEBUG2 (SEND, ("Connection %lx local shut down at %lx, %lx\n", Connection, Connection->CurrentSend.SendSequence, Connection->UnAckedSend.SendSequence));
} else {
Connection->SubState = CONNECTION_SUBSTATE_A_REMOTE_W;
NB_DEBUG2 (SEND, ("Connection %lx remote shut down at %lx\n", Connection, Connection->CurrentSend.SendSequence));
}
//
// Start the timer so we will keep bugging him about
// this. What if he doesn't get a receive down
// quickly -- but this is better than losing his ack
// and then dying. We won't really back off our timer
// because we will keep getting acks, and resetting it.
//
NbiStartRetransmit (Connection);
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
return;
}
}
Request = Connection->CurrentSend.Request;
//
// If we are in this routine then we know that
// we are coming out of IDLE, W_ACK, or W_PACKET
// and we still have the lock held. We also know
// that there is a send request in progress. If
// an ack for none or part of the last packet was
// received, then our send pointers have been
// adjusted to reflect that.
//
//
// First get a packet for the current send.
//
if (!Connection->SendPacketInUse) {
Connection->SendPacketInUse = TRUE;
Packet = PACKET(&Connection->SendPacket);
Reserved = (PNB_SEND_RESERVED)(Packet->ProtocolReserved);
} else {
s = ExInterlockedPopEntrySList(
&Device->SendPacketList,
&NbiGlobalPoolInterlock);
if (s == NULL) {
//
// This function tries to allocate another packet pool.
//
s = NbiPopSendPacket(Device, FALSE);
if (s == NULL) {
//
// It is possible to come in here and already be in
// W_PACKET state -- this is because we may packetize
// when in that state, and rather than always be
// checking that we weren't in W_PACKET, we go
// ahead and check again here.
//
if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PACKET) {
Connection->SubState = CONNECTION_SUBSTATE_A_W_PACKET;
NB_GET_LOCK (&Device->Lock, &DeviceLockHandle);
if (!Connection->OnWaitPacketQueue) {
NbiReferenceConnectionLock (Connection, CREF_W_PACKET);
Connection->OnWaitPacketQueue = TRUE;
InsertTailList(
&Device->WaitPacketConnections,
&Connection->WaitPacketLinkage
);
// NB_INSERT_TAIL_LIST(
// &Device->WaitPacketConnections,
// &Connection->WaitPacketLinkage,
// &Device->Lock);
}
NB_FREE_LOCK (&Device->Lock, DeviceLockHandle);
}
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
return;
}
}
Reserved = CONTAINING_RECORD (s, NB_SEND_RESERVED, PoolLinkage);
Packet = CONTAINING_RECORD (Reserved, NDIS_PACKET, ProtocolReserved[0]);
}
//
// Set this now, we will change it later if needed.
//
Connection->SubState = CONNECTION_SUBSTATE_A_W_ACK;
//
// Save these since they go in this next packet.
//
ThisSendSequence = Connection->CurrentSend.SendSequence;
ThisOffset = (USHORT)Connection->CurrentSend.MessageOffset;
//
// Now see if we need to copy the buffer chain.
//
PacketSize = Connection->MaximumPacketSize;
if (Connection->CurrentSend.MessageOffset + PacketSize >= Connection->CurrentMessageLength) {
PacketSize = Connection->CurrentMessageLength - Connection->CurrentSend.MessageOffset;
if ((Connection->CurrentSend.MessageOffset == 0) &&
(!Connection->SendBufferInUse)) {
//
// If the entire send remaining fits in one packet,
// and this is also the first packet in the send,
// then the entire send fits in one packet and
// we don't need to build a duplicate buffer chain.
//
BufferChain = Connection->CurrentSend.Buffer;
Reserved->u.SR_CO.NoNdisBuffer = TRUE;
Connection->CurrentSend.Buffer = NULL;
Connection->CurrentSend.BufferOffset = 0;
Connection->CurrentSend.MessageOffset = Connection->CurrentMessageLength;
Connection->CurrentSend.Request = NULL;
++Connection->CurrentSend.SendSequence;
Connection->SendBufferInUse = TRUE;
if (Connection->NewNetbios) {
if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) ||
((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) &
TDI_SEND_NO_RESPONSE_EXPECTED)) { // optimize this check
ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK;
} else {
ConnectionControlFlag = NB_CONTROL_EOM;
}
Connection->PiggybackAckTimeout = FALSE;
} else {
ConnectionControlFlag = NB_CONTROL_SEND_ACK;
}
if (BufferChain != NULL) {
NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), user buffer\n",
Reserved, Connection,
Connection->CurrentSend.SendSequence,
Connection->CurrentSend.MessageOffset));
NdisChainBufferAtBack (Packet, BufferChain);
} else {
NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no buffer\n",
Reserved, Connection,
Connection->CurrentSend.SendSequence,
Connection->CurrentSend.MessageOffset));
}
goto GotBufferChain;
}
}
//
// We need to build a partial buffer chain. In the case
// where the current request is a partial one, we may
// build this from the ndis buffer chains of several
// requests.
//
if (PacketSize > 0) {
DesiredLength = PacketSize;
NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), allocate buffer\n",
Reserved, Connection,
Connection->CurrentSend.SendSequence,
Connection->CurrentSend.MessageOffset));
while (TRUE) {
Status = NbiBuildBufferChainFromBufferChain (
Device->NdisBufferPoolHandle,
Connection->CurrentSend.Buffer,
Connection->CurrentSend.BufferOffset,
DesiredLength,
&BufferChain,
&Connection->CurrentSend.Buffer,
&Connection->CurrentSend.BufferOffset,
&ActualLength);
if (Status != STATUS_SUCCESS) {
PNDIS_BUFFER CurBuffer, TmpBuffer;
NB_DEBUG2 (SEND, ("Allocate buffer chain failed for packet %lx\n", Reserved));
//
// We could not allocate resources for this send.
// We'll put the connection on the packetize
// queue and hope we get more resources later.
//
NbiReferenceConnectionSync (Connection, CREF_PACKETIZE);
CTEAssert (!Connection->OnPacketizeQueue);
Connection->OnPacketizeQueue = TRUE;
//
// Connection->CurrentSend can stay where it is.
//
NB_INSERT_TAIL_LIST(
&Device->PacketizeConnections,
&Connection->PacketizeLinkage,
&Device->Lock);
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
//
// Free any buffers we have allocated on previous calls
// to BuildBufferChain inside this same while(TRUE) loop,
// then free the packet.
//
CurBuffer = NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer));
while (CurBuffer) {
TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer);
NdisFreeBuffer (CurBuffer);
CurBuffer = TmpBuffer;
}
NDIS_BUFFER_LINKAGE (NDIS_BUFFER_LINKAGE(Reserved->HeaderBuffer)) = NULL;
NdisRecalculatePacketCounts (Packet);
if (Reserved->OwnedByConnection) {
Connection->SendPacketInUse = FALSE;
} else {
NbiPushSendPacket(Reserved);
}
return;
}
NdisChainBufferAtBack (Packet, BufferChain);
Connection->CurrentSend.MessageOffset += ActualLength;
DesiredLength -= ActualLength;
if (DesiredLength == 0) {
//
// We have gotten enough data for our packet.
//
if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) {
Connection->CurrentSend.Request = NULL;
}
break;
}
//
// We ran out of buffer chain on this send, which means
// that we must have another one behind it (since we
// don't start packetizing partial sends until all of
// them are queued).
//
Request = REQUEST_SINGLE_LINKAGE(Request);
if (Request == NULL) {
KeBugCheck (NDIS_INTERNAL_ERROR);
}
Connection->CurrentSend.Request = Request;
Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request);
Connection->CurrentSend.BufferOffset = 0;
}
} else {
//
// This is a zero-length send (in general we will go
// through the code before the if that uses the user's
// buffer, but not on a resend).
//
Connection->CurrentSend.Buffer = NULL;
Connection->CurrentSend.BufferOffset = 0;
CTEAssert (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength);
Connection->CurrentSend.Request = NULL;
NB_DEBUG2 (SEND, ("Send packet %lx on %lx (%d/%d), no alloc buf\n",
Reserved, Connection,
Connection->CurrentSend.SendSequence,
Connection->CurrentSend.MessageOffset));
}
Reserved->u.SR_CO.NoNdisBuffer = FALSE;
if (Connection->NewNetbios) {
++Connection->CurrentSend.SendSequence;
if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) {
if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) {
ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK;
} else if ((ThisSendSequence == Connection->RemoteRcvSequenceMax) ||
((((PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request))->SendFlags) &
TDI_SEND_NO_RESPONSE_EXPECTED)) { // optimize this check
ConnectionControlFlag = NB_CONTROL_EOM | NB_CONTROL_SEND_ACK;
} else {
ConnectionControlFlag = NB_CONTROL_EOM;
}
Connection->PiggybackAckTimeout = FALSE;
} else if (((USHORT)(Connection->CurrentSend.SendSequence - Connection->UnAckedSend.SendSequence)) >= Connection->SendWindowSize) {
ConnectionControlFlag = NB_CONTROL_SEND_ACK;
} else if (ThisSendSequence == Connection->RemoteRcvSequenceMax) {
ConnectionControlFlag = NB_CONTROL_SEND_ACK;
} else {
ConnectionControlFlag = 0;
ExitAfterSend = FALSE;
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
}
} else {
ConnectionControlFlag = NB_CONTROL_SEND_ACK;
if (Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) {
++Connection->CurrentSend.SendSequence;
}
}
GotBufferChain:
//
// We have a packet and a buffer chain, there are
// no other resources required for a send so we can
// fill in the header and go.
//
CTEAssert (Reserved->SendInProgress == FALSE);
Reserved->SendInProgress = TRUE;
Reserved->Type = SEND_TYPE_SESSION_DATA;
Reserved->u.SR_CO.Connection = Connection;
Reserved->u.SR_CO.Request = Connection->FirstMessageRequest;
PacketLength = PacketSize + sizeof(NB_CONNECTION);
Reserved->u.SR_CO.PacketLength = PacketLength;
Header = (NB_CONNECTION UNALIGNED *)
(&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
RtlCopyMemory((PVOID)&Header->IpxHeader, &Connection->RemoteHeader, sizeof(IPX_HEADER));
Header->IpxHeader.PacketLength[0] = (UCHAR)(PacketLength / 256);
Header->IpxHeader.PacketLength[1] = (UCHAR)(PacketLength % 256);
Header->IpxHeader.PacketType = 0x04;
//
// Now fill in the Netbios header. Put this in
// a contiguous buffer in the connection ?
//
Header->Session.ConnectionControlFlag = ConnectionControlFlag;
Header->Session.DataStreamType = NB_CMD_SESSION_DATA;
Header->Session.SourceConnectionId = Connection->LocalConnectionId;
Header->Session.DestConnectionId = Connection->RemoteConnectionId;
Header->Session.SendSequence = ThisSendSequence;
Header->Session.TotalDataLength = (USHORT)Connection->CurrentMessageLength;
Header->Session.Offset = ThisOffset;
Header->Session.DataLength = (USHORT)PacketSize;
#if 0
//
// These are set by NbiAssignSequenceAndSend.
//
Header->Session.ReceiveSequence = Connection->ReceiveSequence;
Header->Session.BytesReceived = (USHORT)Connection->CurrentReceive.MessageOffset;
#endif
//
// Reference the request to account for this send.
//
#if DBG
if (REQUEST_REFCOUNT(Request) > 100) {
DbgPrint ("Request %lx (%lx) has high refcount\n",
Connection, Request);
DbgBreakPoint();
}
#endif
++REQUEST_REFCOUNT (Request);
++Device->TempFramesSent;
Device->TempFrameBytesSent += PacketSize;
//
// Start the timer.
//
NbiStartRetransmit (Connection);
//
// This frees the lock. IPX will adjust the length of
// the first buffer correctly.
//
NbiAssignSequenceAndSend(
Connection,
Packet
NB_LOCK_HANDLE_ARG(LockHandle));
if (!ExitAfterSend) {
//
// Did we need to reference the connection until we
// get the lock back??
//
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
if ((Connection->State == CONNECTION_STATE_ACTIVE) &&
(Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) {
//
// Jump back to the beginning of the function to
// repacketize.
goto SendAnotherPacket;
} else {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
}
} /* NbiPacketizeSend */
VOID
NbiAdjustSendWindow(
IN PCONNECTION Connection
)
/*++
Routine Description:
This routine adjusts a connection's send window if needed. It is
assumed that we just got an ack for a full send window.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT HELD.
Arguments:
Connection - The connection.
Return Value:
None.
--*/
{
if (Connection->RetransmitThisWindow) {
//
// Move it down. Check if this keeps happening.
//
if (Connection->SendWindowSize > 2) {
--Connection->SendWindowSize;
NB_DEBUG2 (SEND_WINDOW, ("Lower window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence));
}
if (Connection->SendWindowIncrease) {
//
// We just increased the window.
//
++Connection->IncreaseWindowFailures;
NB_DEBUG2 (SEND_WINDOW, ("%d consecutive increase failues on %lx (%lx)\n", Connection->IncreaseWindowFailures, Connection, Connection->CurrentSend.SendSequence));
if (Connection->IncreaseWindowFailures >= 2) {
if (Connection->MaxSendWindowSize > 2) {
//
// Lock ourselves at a smaller window.
//
Connection->MaxSendWindowSize = Connection->SendWindowSize;
NB_DEBUG2 (SEND_WINDOW, ("Lock send window at %d on %lx (%lx)\n", Connection->MaxSendWindowSize, Connection, Connection->CurrentSend.SendSequence));
}
Connection->IncreaseWindowFailures = 0;
}
Connection->SendWindowIncrease = FALSE;
}
} else {
//
// Increase it if allowed, and make a note
// in case this increase causes problems in
// the next window.
//
if (Connection->SendWindowSize < Connection->MaxSendWindowSize) {
++Connection->SendWindowSize;
NB_DEBUG2 (SEND_WINDOW, ("Raise window to %d on %lx (%lx)\n", Connection->SendWindowSize, Connection, Connection->CurrentSend.SendSequence));
Connection->SendWindowIncrease = TRUE;
} else {
if (Connection->SendWindowIncrease) {
//
// We just increased it and nothing failed,
// which is good.
//
Connection->SendWindowIncrease = FALSE;
Connection->IncreaseWindowFailures = 0;
NB_DEBUG2 (SEND_WINDOW, ("Raised window OK on %lx (%lx)\n", Connection, Connection->CurrentSend.SendSequence));
}
}
}
//
// This controls when we'll check this again.
//
Connection->SendWindowSequenceLimit += Connection->SendWindowSize;
} /* NbiAdjustSendWindow */
VOID
NbiReframeConnection(
IN PCONNECTION Connection,
IN USHORT ReceiveSequence,
IN USHORT BytesReceived,
IN BOOLEAN Resend
IN NB_LOCK_HANDLE_PARAM(LockHandle)
)
/*++
Routine Description:
This routine is called when we have gotten an ack
for some data. It completes any sends that have
been acked, and if needed modifies the current send
pointer and queues the connection for repacketizing.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT RELEASED.
Arguments:
Connection - The connection.
ReceiveSequence - The receive sequence from the remote.
BytesReceived - The number of bytes received in this message.
Resend - If it is OK to resend based on this packet.
LockHandle - The handle with which Connection->Lock was acquired.
Return Value:
None.
--*/
{
PREQUEST Request, TmpRequest;
PREQUEST RequestToComplete;
PDEVICE Device = NbiDevice;
CTELockHandle CancelLH;
//
// We should change to stop the timer
// only if we go idle, since otherwise we still
// want it running, or will restart it when we
// packetize.
//
//
// See how much is acked here.
//
if ((Connection->CurrentSend.MessageOffset == Connection->CurrentMessageLength) &&
(ReceiveSequence == (USHORT)(Connection->CurrentSend.SendSequence)) &&
(Connection->FirstMessageRequest != NULL)) {
// Special check for 0 length send which was not accepted by the remote.
// In this case it will pass the above 3 conditions yet, nothing
// is acked. BUG#10395
if (!Connection->CurrentSend.MessageOffset && Connection->CurrentSend.SendSequence == Connection->UnAckedSend.SendSequence ) {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
return;
}
//
// This acks the entire message.
//
NB_DEBUG2 (SEND, ("Got ack for entire message on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence));
NbiStopRetransmit (Connection);
Connection->CurrentSend.MessageOffset = 0; // Needed?
Connection->UnAckedSend.MessageOffset = 0;
//
// We don't adjust the send window since we likely stopped
// packetizing before we hit it.
//
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
if (Connection->NewNetbios) {
Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac
//
// See if we need to adjust our send window.
//
if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) {
NbiAdjustSendWindow (Connection);
} else {
//
// Advance this, we won't get meaningful results until we
// send a full window in one message.
//
Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize;
}
}
Connection->RetransmitThisWindow = FALSE;
Request = Connection->FirstMessageRequest;
//
// We dequeue these requests from the connection's
// send queue.
//
if (Connection->FirstMessageRequest == Connection->LastMessageRequest) {
REQUEST_SINGLE_LINKAGE (Request) = NULL;
} else {
Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest);
REQUEST_SINGLE_LINKAGE (Connection->LastMessageRequest) = NULL;
}
#if DBG
if (REQUEST_REFCOUNT(Request) > 100) {
DbgPrint ("Request %lx (%lx) has high refcount\n",
Connection, Request);
DbgBreakPoint();
}
#endif
if (--REQUEST_REFCOUNT(Request) == 0) {
RequestToComplete = Request;
} else {
//
// There are still sends pending, this will get
// completed when the last send completes. Since
// we have already unlinked the request from the
// connection's send queue we can do this without
// any locks.
//
RequestToComplete = NULL;
}
//
// Now see if there is a send to activate.
//
NbiRestartConnection (Connection);
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
//
// Now complete any requests we need to.
//
while (RequestToComplete != NULL) {
TmpRequest = REQUEST_SINGLE_LINKAGE (RequestToComplete);
REQUEST_STATUS (RequestToComplete) = STATUS_SUCCESS;
NB_GET_CANCEL_LOCK( &CancelLH );
IoSetCancelRoutine (RequestToComplete, (PDRIVER_CANCEL)NULL);
NB_FREE_CANCEL_LOCK( CancelLH );
NbiCompleteRequest (RequestToComplete);
NbiFreeRequest (Device, RequestToComplete);
++Connection->ConnectionInfo.TransmittedTsdus;
RequestToComplete = TmpRequest;
NbiDereferenceConnection (Connection, CREF_SEND);
}
} else if ((ReceiveSequence == Connection->CurrentSend.SendSequence) &&
(Connection->NewNetbios || (BytesReceived == Connection->CurrentSend.MessageOffset)) &&
(Connection->CurrentSend.Request != NULL)) {
//
// This acks whatever we sent last time, and we are
// not done packetizing this send, so we can repacketize.
// With SendSequence changing as it does now,
// don't need the CurrentSend.Request check???
//
NB_DEBUG2 (SEND, ("Got full ack on %lx (%d)\n", Connection, Connection->CurrentSend.SendSequence));
NbiStopRetransmit (Connection);
if (Connection->NewNetbios) {
//
// If we are waiting for a window, and this does not open it
// anymore, then we don't reset our timers/retries.
//
if (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) {
if (Connection->RemoteRcvSequenceMax != BytesReceived) {
Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
}
//
// Advance this, we won't get meaningful results until we
// send a full window in one message.
//
Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize;
} else {
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac
if (((SHORT)(Connection->CurrentSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) {
NbiAdjustSendWindow (Connection);
} else {
//
// Advance this, we won't get meaningful results until we
// send a full window in one message.
//
Connection->SendWindowSequenceLimit = Connection->CurrentSend.SendSequence + Connection->SendWindowSize;
}
}
} else {
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
}
Connection->RetransmitThisWindow = FALSE;
Connection->UnAckedSend = Connection->CurrentSend;
if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) {
//
// We may be on if this ack is duplicated.
//
NbiReferenceConnectionSync (Connection, CREF_PACKETIZE);
CTEAssert(!Connection->OnPacketizeQueue);
Connection->OnPacketizeQueue = TRUE;
NB_INSERT_TAIL_LIST(
&Device->PacketizeConnections,
&Connection->PacketizeLinkage,
&Device->Lock);
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
}
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
} else if( Connection->FirstMessageRequest ) {
//
// This acked part of the current send. If the
// remote is requesting a resend then we advance
// the current send location by the amount
// acked and resend from there. If he does
// not want a resend, just ignore this.
//
// We repacketize immediately because we have
// backed up the pointer, and this would
// cause us to ignore an ack for the amount
// sent. Since we don't release the lock
// until we have packetized, the current
// pointer will be advanced past there.
//
// If he is acking more than we sent, we
// ignore this -- the remote is confused and there
// is nothing much we can do.
//
if (Resend) {
if (Connection->NewNetbios &&
(((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) &&
(ReceiveSequence >= Connection->UnAckedSend.SendSequence) &&
(ReceiveSequence < Connection->CurrentSend.SendSequence)) ||
((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) &&
((ReceiveSequence >= Connection->UnAckedSend.SendSequence) ||
(ReceiveSequence < Connection->CurrentSend.SendSequence))))) {
BOOLEAN SomethingAcked = (BOOLEAN)
(ReceiveSequence != Connection->UnAckedSend.SendSequence);
//
// New netbios and the receive sequence is valid.
//
NbiStopRetransmit (Connection);
//
// Advance our unacked pointer by the amount
// acked in this response.
//
NbiAdvanceUnAckedBySequence(
Connection,
ReceiveSequence);
Connection->RetransmitThisWindow = TRUE;
++Connection->ConnectionInfo.TransmissionErrors;
++Device->Statistics.DataFramesResent;
ADD_TO_LARGE_INTEGER(
&Device->Statistics.DataFrameBytesResent,
Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset);
//
// Packetize from that point on.
//
Connection->CurrentSend = Connection->UnAckedSend;
//
// If anything was acked, then reset the retry count.
//
if (SomethingAcked) {
//
// See if we need to adjust our send window.
//
if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) {
NbiAdjustSendWindow (Connection);
}
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
}
Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac
//
// Now packetize. This will set the state to
// something meaningful and release the lock.
//
if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) {
NbiPacketizeSend(
Connection
NB_LOCK_HANDLE_ARG(LockHandle)
);
} else {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} else if (!Connection->NewNetbios &&
((ReceiveSequence == Connection->UnAckedSend.SendSequence) &&
(BytesReceived <= Connection->CurrentSend.MessageOffset))) {
ULONG BytesAcked =
BytesReceived - Connection->UnAckedSend.MessageOffset;
//
// Old netbios.
//
NbiStopRetransmit (Connection);
//
// Advance our unacked pointer by the amount
// acked in this response.
//
NbiAdvanceUnAckedByBytes(
Connection,
BytesAcked);
++Connection->ConnectionInfo.TransmissionErrors;
++Device->Statistics.DataFramesResent;
ADD_TO_LARGE_INTEGER(
&Device->Statistics.DataFrameBytesResent,
Connection->CurrentSend.MessageOffset - Connection->UnAckedSend.MessageOffset);
//
// Packetize from that point on.
//
Connection->CurrentSend = Connection->UnAckedSend;
//
// If anything was acked, reset the retry count
//
if ( BytesAcked ) {
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
}
//
// Now packetize. This will set the state to
// something meaningful and release the lock.
//
if (Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE) {
NbiPacketizeSend(
Connection
NB_LOCK_HANDLE_ARG(LockHandle)
);
} else {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} else {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} else {
if (Connection->NewNetbios &&
(((Connection->UnAckedSend.SendSequence < Connection->CurrentSend.SendSequence) &&
(ReceiveSequence >= Connection->UnAckedSend.SendSequence) &&
(ReceiveSequence < Connection->CurrentSend.SendSequence)) ||
((Connection->UnAckedSend.SendSequence > Connection->CurrentSend.SendSequence) &&
((ReceiveSequence >= Connection->UnAckedSend.SendSequence) ||
(ReceiveSequence < Connection->CurrentSend.SendSequence))))) {
BOOLEAN SomethingAcked = (BOOLEAN)
(ReceiveSequence != Connection->UnAckedSend.SendSequence);
//
// New netbios and the receive sequence is valid. We advance
// the back of our send window, but we don't repacketize.
//
//
// Advance our unacked pointer by the amount
// acked in this response.
//
NbiAdvanceUnAckedBySequence(
Connection,
ReceiveSequence);
Connection->RemoteRcvSequenceMax = BytesReceived; // really RcvSeqMac
//
// If anything was acked, then reset the retry count.
//
if (SomethingAcked) {
//
// See if we need to adjust our send window.
//
if (((SHORT)(Connection->UnAckedSend.SendSequence - Connection->SendWindowSequenceLimit)) >= 0) {
NbiAdjustSendWindow (Connection);
}
Connection->Retries = NbiDevice->KeepAliveCount;
Connection->CurrentRetransmitTimeout = Connection->BaseRetransmitTimeout;
//
// Now packetize. This will set the state to
// something meaningful and release the lock.
//
if ((Connection->CurrentSend.Request != NULL) &&
(Connection->SubState != CONNECTION_SUBSTATE_A_PACKETIZE)) {
NbiReferenceConnectionSync (Connection, CREF_PACKETIZE);
CTEAssert(!Connection->OnPacketizeQueue);
Connection->OnPacketizeQueue = TRUE;
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
NB_INSERT_TAIL_LIST(
&Device->PacketizeConnections,
&Connection->PacketizeLinkage,
&Device->Lock);
}
}
}
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} else {
NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
}
} /* NbiReframeConnection */
VOID
NbiRestartConnection(
IN PCONNECTION Connection
)
/*++
Routine Description:
This routine is called when have gotten an ack for
a full message, or received a response to a watchdog
probe, and need to check if the connection should
start packetizing.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT HELD.
Arguments:
Connection - The connection.
Return Value:
None.
--*/
{
PREQUEST Request, TmpRequest;
ULONG TempCount;
PTDI_REQUEST_KERNEL_SEND Parameters;
PDEVICE Device = NbiDevice;
//
// See if there is a send to activate.
//
if (Connection->SendQueue.Head != NULL) {
//
// Take the first send off the queue and make
// it current.
//
Request = Connection->SendQueue.Head;
Connection->SendQueue.Head = REQUEST_SINGLE_LINKAGE (Request);
//
// Cache the information about being EOM
// in a more easily accessible location?
//
Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request);
if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) {
//
// This is a one-request message.
//
Connection->CurrentSend.Request = Request;
Connection->CurrentSend.MessageOffset = 0;
Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (Request);
Connection->CurrentSend.BufferOffset = 0;
Connection->SendBufferInUse = FALSE;
Connection->UnAckedSend = Connection->CurrentSend;
Connection->FirstMessageRequest = Request;
#ifdef RSRC_TIMEOUT_DBG
KeQuerySystemTime(&Connection->FirstMessageRequestTime);
#endif //RSRC_TIMEOUT_DBG
Connection->LastMessageRequest = Request;
Connection->CurrentMessageLength = Parameters->SendLength;
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
NbiReferenceConnectionSync (Connection, CREF_PACKETIZE);
CTEAssert (!Connection->OnPacketizeQueue);
Connection->OnPacketizeQueue = TRUE;
NB_INSERT_TAIL_LIST(
&Device->PacketizeConnections,
&Connection->PacketizeLinkage,
&Device->Lock);
} else {
//
// This is a multiple-request message. We scan
// to see if we have the end of message received
// yet.
//
TempCount = Parameters->SendLength;
TmpRequest = Request;
Request = REQUEST_SINGLE_LINKAGE(Request);
while (Request != NULL) {
TempCount += Parameters->SendLength;
Parameters = (PTDI_REQUEST_KERNEL_SEND)REQUEST_PARAMETERS(Request);
if ((Parameters->SendFlags & TDI_SEND_PARTIAL) == 0) {
Connection->CurrentSend.Request = TmpRequest;
Connection->CurrentSend.MessageOffset = 0;
Connection->CurrentSend.Buffer = REQUEST_NDIS_BUFFER (TmpRequest);
Connection->CurrentSend.BufferOffset = 0;
Connection->SendBufferInUse = FALSE;
Connection->UnAckedSend = Connection->CurrentSend;
Connection->FirstMessageRequest = TmpRequest;
Connection->LastMessageRequest = Request;
#ifdef RSRC_TIMEOUT_DBG
KeQuerySystemTime(&Connection->FirstMessageRequestTime);
#endif //RSRC_TIMEOUT_DBG
Connection->CurrentMessageLength = TempCount;
Connection->SubState = CONNECTION_SUBSTATE_A_PACKETIZE;
NbiReferenceConnectionSync (Connection, CREF_PACKETIZE);
CTEAssert (!Connection->OnPacketizeQueue);
Connection->OnPacketizeQueue = TRUE;
NB_INSERT_TAIL_LIST(
&Device->PacketizeConnections,
&Connection->PacketizeLinkage,
&Device->Lock);
break;
}
Request = REQUEST_SINGLE_LINKAGE(Request);
}
if (Request == NULL) {
Connection->SubState = CONNECTION_SUBSTATE_A_W_EOR;
}
}
} else {
Connection->FirstMessageRequest = NULL;
Connection->SubState = CONNECTION_SUBSTATE_A_IDLE;
NbiStartWatchdog (Connection);
}
} /* NbiRestartConnection */
VOID
NbiAdvanceUnAckedByBytes(
IN PCONNECTION Connection,
IN ULONG BytesAcked
)
/*++
Routine Description:
This routine advances the Connection->UnAckedSend
send pointer by the specified number of bytes. It
assumes that there are enough send requests to
handle the number specified.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT HELD.
Arguments:
Connection - The connection.
BytesAcked - The number of bytes acked.
Return Value:
NTSTATUS - status of operation.
--*/
{
ULONG CurSendBufferLength;
ULONG BytesLeft = BytesAcked;
ULONG TempBytes;
while (BytesLeft > 0) {
NdisQueryBufferSafe (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength, HighPagePriority);
//
// See if bytes acked ends within the current buffer.
//
if (Connection->UnAckedSend.BufferOffset + BytesLeft <
CurSendBufferLength) {
Connection->UnAckedSend.BufferOffset += BytesLeft;
Connection->UnAckedSend.MessageOffset += BytesLeft;
break;
} else {
TempBytes = CurSendBufferLength - Connection->UnAckedSend.BufferOffset;
BytesLeft -= TempBytes;
Connection->UnAckedSend.MessageOffset += TempBytes;
//
// No, so advance the buffer.
//
Connection->UnAckedSend.BufferOffset = 0;
Connection->UnAckedSend.Buffer =
NDIS_BUFFER_LINKAGE (Connection->UnAckedSend.Buffer);
//
// Is there a next buffer in this request?
//
if (Connection->UnAckedSend.Buffer == NULL) {
//
// No, so advance the request unless we are done.
//
if (BytesLeft == 0) {
return;
}
Connection->UnAckedSend.Request =
REQUEST_SINGLE_LINKAGE(Connection->UnAckedSend.Request);
if (Connection->UnAckedSend.Request == NULL) {
KeBugCheck (NDIS_INTERNAL_ERROR);
}
Connection->UnAckedSend.Buffer =
REQUEST_NDIS_BUFFER (Connection->UnAckedSend.Request);
}
}
}
} /* NbiAdvanceUnAckedByBytes */
VOID
NbiAdvanceUnAckedBySequence(
IN PCONNECTION Connection,
IN USHORT ReceiveSequence
)
/*++
Routine Description:
This routine advances the Connection->UnAckedSend
send pointer so that the next packet to send will be
the correct one for ReceiveSequence. UnAckedSend
must point to a known valid combination. It
assumes that there are enough send requests to
handle the sequence specified.
NOTE: THIS FUNCTION IS CALLED WITH CONNECTION->LOCK HELD
AND RETURNS WITH IT HELD.
Arguments:
Connection - The connection.
Return Value:
NTSTATUS - status of operation.
--*/
{
USHORT PacketsAcked;
//
// Fix this to account for partial sends, where
// we might not have used the max. for all packets.
//
PacketsAcked = ReceiveSequence - Connection->UnAckedSend.SendSequence;
NbiAdvanceUnAckedByBytes(
Connection,
PacketsAcked * Connection->MaximumPacketSize);
Connection->UnAckedSend.SendSequence += PacketsAcked;
} /* NbiAdvanceUnAckedBySequence */
VOID
NbiCancelSend(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to cancel a send
The request is found on the connection's send queue.
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.
--*/
{
PCONNECTION Connection;
PREQUEST Request = (PREQUEST)Irp;
NB_DEFINE_LOCK_HANDLE (LockHandle)
NB_DEFINE_SYNC_CONTEXT (SyncContext)
CTEAssert ((REQUEST_MAJOR_FUNCTION(Request) == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
(REQUEST_MINOR_FUNCTION(Request) == TDI_SEND));
CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE);
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
//
// Just stop the connection, that will tear down any
// sends.
//
// Do we care about cancelling non-active
// sends without stopping the connection??
//
NbiReferenceConnectionSync (Connection, CREF_CANCEL);
IoReleaseCancelSpinLock (Irp->CancelIrql);
NB_BEGIN_SYNC (&SyncContext);
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
//
// This frees the lock, cancels any sends, etc.
//
NbiStopConnection(
Connection,
STATUS_CANCELLED
NB_LOCK_HANDLE_ARG (LockHandle));
NbiDereferenceConnection (Connection, CREF_CANCEL);
NB_END_SYNC (&SyncContext);
} /* NbiCancelSend */
NTSTATUS
NbiBuildBufferChainFromBufferChain (
IN NDIS_HANDLE BufferPoolHandle,
IN PNDIS_BUFFER CurrentSourceBuffer,
IN ULONG CurrentByteOffset,
IN ULONG DesiredLength,
OUT PNDIS_BUFFER *DestinationBuffer,
OUT PNDIS_BUFFER *NewSourceBuffer,
OUT ULONG *NewByteOffset,
OUT ULONG *ActualLength
)
/*++
Routine Description:
This routine is called to build an NDIS_BUFFER chain from a source
NDIS_BUFFER 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.
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.
Environment:
Arguments:
BufferPoolHandle - The buffer pool to allocate buffers from.
CurrentSourceBuffer - Points to the start of the NDIS_BUFFER chain
from which to draw the packet.
CurrentByteOffset - Offset within this NDIS_BUFFER to start the packet at.
DesiredLength - The number of bytes to insert into the packet.
DestinationBuffer - returned pointer to the NDIS_BUFFER chain describing
the packet.
NewSourceBuffer - returned pointer to the NDIS_BUFFER that would
be used for the next byte of packet. NULL if the source NDIS_BUFFER
chain was exhausted.
NewByteOffset - returned offset into the NewSourceBuffer for the next byte
of packet. NULL if the source NDIS_BUFFER chain was exhausted.
ActualLength - The actual length of the data copied.
Return Value:
STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded
and was the correct length.
STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while
building the destination chain.
--*/
{
ULONG AvailableBytes;
ULONG CurrentByteCount;
ULONG BytesCopied;
PNDIS_BUFFER OldNdisBuffer;
PNDIS_BUFFER NewNdisBuffer;
NDIS_STATUS NdisStatus;
OldNdisBuffer = CurrentSourceBuffer;
NdisQueryBufferSafe (OldNdisBuffer, NULL, &CurrentByteCount, HighPagePriority);
AvailableBytes = CurrentByteCount - CurrentByteOffset;
if (AvailableBytes > DesiredLength) {
AvailableBytes = DesiredLength;
}
//
// Build the first NDIS_BUFFER, which could conceivably be the only one...
//
NdisCopyBuffer(
&NdisStatus,
&NewNdisBuffer,
BufferPoolHandle,
OldNdisBuffer,
CurrentByteOffset,
AvailableBytes);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
*NewSourceBuffer = CurrentSourceBuffer;
*NewByteOffset = CurrentByteOffset;
return STATUS_INSUFFICIENT_RESOURCES;
}
*DestinationBuffer = NewNdisBuffer;
BytesCopied = AvailableBytes;
//
// Was the first NDIS_BUFFER enough data.
//
if (BytesCopied == DesiredLength) {
if (CurrentByteOffset + AvailableBytes == CurrentByteCount) {
*NewSourceBuffer = CurrentSourceBuffer->Next;
*NewByteOffset = 0;
} else {
*NewSourceBuffer = CurrentSourceBuffer;
*NewByteOffset = CurrentByteOffset + AvailableBytes;
}
*ActualLength = BytesCopied;
return STATUS_SUCCESS;
}
if (CurrentSourceBuffer->Next == NULL) {
*NewSourceBuffer = NULL;
*NewByteOffset = 0;
*ActualLength = BytesCopied;
return STATUS_SUCCESS;
}
//
// Need more data, so follow the in Mdl chain to create a packet.
//
OldNdisBuffer = OldNdisBuffer->Next;
NdisQueryBufferSafe (OldNdisBuffer, NULL, &CurrentByteCount, HighPagePriority);
while (OldNdisBuffer != NULL) {
AvailableBytes = DesiredLength - BytesCopied;
if (AvailableBytes > CurrentByteCount) {
AvailableBytes = CurrentByteCount;
}
NdisCopyBuffer(
&NdisStatus,
&(NDIS_BUFFER_LINKAGE(NewNdisBuffer)),
BufferPoolHandle,
OldNdisBuffer,
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 (*DestinationBuffer != NULL) {
NewNdisBuffer = NDIS_BUFFER_LINKAGE(*DestinationBuffer);
NdisFreeBuffer (*DestinationBuffer);
*DestinationBuffer = NewNdisBuffer;
}
*NewByteOffset = CurrentByteOffset;
*NewSourceBuffer = CurrentSourceBuffer;
return STATUS_INSUFFICIENT_RESOURCES;
}
NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer);
BytesCopied += AvailableBytes;
if (BytesCopied == DesiredLength) {
if (AvailableBytes == CurrentByteCount) {
*NewSourceBuffer = OldNdisBuffer->Next;
*NewByteOffset = 0;
} else {
*NewSourceBuffer = OldNdisBuffer;
*NewByteOffset = AvailableBytes;
}
*ActualLength = BytesCopied;
return STATUS_SUCCESS;
}
OldNdisBuffer = OldNdisBuffer->Next;
NdisQueryBufferSafe (OldNdisBuffer, NULL, &CurrentByteCount, HighPagePriority);
}
//
// We ran out of source buffer chain.
//
*NewSourceBuffer = NULL;
*NewByteOffset = 0;
*ActualLength = BytesCopied;
return STATUS_SUCCESS;
} /* NbiBuildBufferChainFromBufferChain */