diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/isn/nb/send.c | 2886 |
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 */ + |