summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/isn/nb/send.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/isn/nb/send.c2886
1 files changed, 2886 insertions, 0 deletions
diff --git a/private/ntos/tdi/isn/nb/send.c b/private/ntos/tdi/isn/nb/send.c
new file mode 100644
index 000000000..a4443c73c
--- /dev/null
+++ b/private/ntos/tdi/isn/nb/send.c
@@ -0,0 +1,2886 @@
+/*++
+
+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
+
+
+
+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;
+ PVOID SecondBufferMemory;
+ 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).
+ //
+
+FunctionStart:;
+
+ ++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;
+
+#if !defined(_PNP_POWER)
+ if ((Reserved->u.SR_NF.CurrentNicId) &&
+ (Reserved->u.SR_NF.CurrentNicId < Device->MaximumNicId)) {
+
+ NB_CONNECTIONLESS UNALIGNED * Header;
+ IPX_LOCAL_TARGET TempLocalTarget;
+
+ //
+ // This is a name frame being sent to every address, so
+ // resent it to the next NIC ID. We hold the address
+ // reference through this send.
+ //
+
+ CTEAssert (Address != NULL);
+
+ ++Reserved->u.SR_NF.CurrentNicId;
+
+ //
+ // Fill in the IPX header -- the default header has the broadcast
+ // address on net 0 as the destination IPX address.
+ //
+
+ Header = (NB_CONNECTIONLESS UNALIGNED *)
+ (&Reserved->Header[Device->Bind.IncludedHeaderOffset]);
+ RtlCopyMemory((PVOID)&Header->IpxHeader, &Device->ConnectionlessHeader, sizeof(IPX_HEADER));
+ Header->IpxHeader.PacketLength[0] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) / 256;
+ Header->IpxHeader.PacketLength[1] = (sizeof(IPX_HEADER)+sizeof(NB_NAME_FRAME)) % 256;
+
+ Header->IpxHeader.PacketType = (UCHAR)(Device->Internet ? 0x014 : 0x04);
+
+ //
+ // Now fill in the Netbios header.
+ //
+
+ RtlZeroMemory (Header->NameFrame.RoutingInfo, 32);
+ Header->NameFrame.ConnectionControlFlag = 0x00;
+ Header->NameFrame.DataStreamType = Reserved->u.SR_NF.DataStreamType;
+ Header->NameFrame.NameTypeFlag = Reserved->u.SR_NF.NameTypeFlag;
+
+ //
+ // This is not a name in use frame so DataStreamType2
+ // is the same as DataStreamType.
+ //
+
+ Header->NameFrame.DataStreamType2 = Reserved->u.SR_NF.DataStreamType;
+
+ RtlCopyMemory(
+ Header->NameFrame.Name,
+ Address->NetbiosAddress.NetbiosName,
+ 16);
+
+ //
+ // Now send the frame (because it is all in the first segment,
+ // IPX will adjust the length of the buffer correctly).
+ //
+
+ TempLocalTarget.NicId = Reserved->u.SR_NF.CurrentNicId;
+ RtlCopyMemory (TempLocalTarget.MacAddress, BroadcastAddress, 6);
+
+ NdisAdjustBufferLength(NB_GET_NBHDR_BUFF(Packet), sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME));
+ if ((Status =
+ (*Device->Bind.SendHandler)(
+ &TempLocalTarget,
+ Packet,
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME),
+ sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME))) != STATUS_PENDING) {
+
+ goto FunctionStart;
+
+ }
+
+ return;
+
+ }
+#endif !_PNP_POWER
+
+ 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);
+ NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength);
+ CTEAssert (SecondBufferLength == sizeof(NB_SESSION_INIT));
+
+ NdisFreeBuffer(SecondBuffer);
+ NbiFreeMemory (SecondBufferMemory, sizeof(NB_SESSION_INIT), MEMORY_CONNECTION, "Session Initialize");
+
+ 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 {
+
+ 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 + 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) {
+
+ goto FunctionStart;
+ }
+
+ }
+
+ 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);
+ NdisQueryBuffer (SecondBuffer, &SecondBufferMemory, &SecondBufferLength);
+
+ NdisFreeBuffer(SecondBuffer);
+ NbiFreeMemory (SecondBufferMemory, Reserved->u.SR_AS.ActualBufferLength, MEMORY_STATUS, "Adapter Status");
+
+ 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;
+
+ //
+ // 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. BUGBUG: 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)) { // BUGBUG: 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)) { // BUGBUG: 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. BUGBUG: 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) {
+
+ //
+ // BUGBUG: 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;
+
+
+ //
+ // BUGBUG: 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; // BUGBUG: 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.
+ // BUGBUG: 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.
+ //
+ // BUGBUG: 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);
+
+ //
+ // BUGBUG: 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) {
+
+ NdisQueryBuffer (Connection->UnAckedSend.Buffer, NULL, &CurSendBufferLength);
+
+ //
+ // 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;
+
+ //
+ // BUGBUG: 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.
+ //
+ // BUGBUG: 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;
+ NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount);
+
+ 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;
+ NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount);
+
+ 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;
+ NdisQueryBuffer (OldNdisBuffer, NULL, &CurrentByteCount);
+
+ }
+
+ //
+ // We ran out of source buffer chain.
+ //
+
+ *NewSourceBuffer = NULL;
+ *NewByteOffset = 0;
+ *ActualLength = BytesCopied;
+ return STATUS_SUCCESS;
+
+} /* NbiBuildBufferChainFromBufferChain */
+