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
89 KiB
2888 lines
89 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;
|
|
PSLIST_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 */
|
|
|