/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: receive.c Abstract: This module contains the code to handle receive indication and posted receives 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 // // This routine is a no-op to put in the NbiCallbacks table so // we can avoid checking for runt session frames (this is because // of how the if is structure below). // VOID NbiProcessSessionRunt( IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR PacketBuffer, IN UINT PacketSize ) { return; } NB_CALLBACK_NO_TRANSFER NbiCallbacksNoTransfer[] = { NbiProcessFindName, NbiProcessNameRecognized, NbiProcessAddName, NbiProcessAddName, // processes name in use frames also NbiProcessDeleteName, NbiProcessSessionRunt, // in case get a short session packet NbiProcessSessionEnd, NbiProcessSessionEndAck, NbiProcessStatusQuery }; #ifdef RSRC_TIMEOUT_DBG VOID NbiProcessDeathPacket( IN NDIS_HANDLE MacBindingHandle, IN NDIS_HANDLE MacReceiveContext, IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR LookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT LookaheadBufferOffset, IN UINT PacketSize ) /*++ Routine Description: This routine handles NB_CMD_SESSION_DATA frames. Arguments: MacBindingHandle - A handle to use when calling NdisTransferData. MacReceiveContext - A context to use when calling NdisTransferData. RemoteAddress - The local target this packet was received from. MacOptions - The MAC options for the underlying NDIS binding. LookaheadBuffer - The lookahead buffer, starting at the IPX header. LookaheadBufferSize - The length of the lookahead data. LookaheadBufferOffset - The offset to add when calling NdisTransferData. PacketSize - The total length of the packet, starting at the IPX header. Return Value: None. --*/ { NB_CONNECTION UNALIGNED * Conn = (NB_CONNECTION UNALIGNED *)LookaheadBuffer; NB_SESSION UNALIGNED * Sess = (NB_SESSION UNALIGNED *)(&Conn->Session); PCONNECTION Connection; PDEVICE Device = NbiDevice; ULONG Hash; NB_DEFINE_LOCK_HANDLE (LockHandle) DbgPrint("******Received death packet - connid %x\n",Sess->DestConnectionId); if ( !NbiGlobalDebugResTimeout ) { return; } if (Sess->DestConnectionId != 0xffff) { // // This is an active connection, find it using // our session id. // Hash = (Sess->DestConnectionId & CONNECTION_HASH_MASK) >> CONNECTION_HASH_SHIFT; NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); Connection = Device->ConnectionHash[Hash].Connections; while (Connection != NULL) { if (Connection->LocalConnectionId == Sess->DestConnectionId) { break; } Connection = Connection->NextConnection; } if (Connection == NULL) { DbgPrint("********No Connection found with %x id\n",Sess->DestConnectionId); NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); return; } DbgPrint("******Received death packet on conn %lx from <%.16s>\n",Connection,Connection->RemoteName); DbgBreakPoint(); NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); } } #endif //RSRC_TIMEOUT_DBG VOID NbiReceive( IN NDIS_HANDLE MacBindingHandle, IN NDIS_HANDLE MacReceiveContext, IN PIPX_LOCAL_TARGET RemoteAddress, IN ULONG MacOptions, IN PUCHAR LookaheadBuffer, IN UINT LookaheadBufferSize, IN UINT LookaheadBufferOffset, IN UINT PacketSize ) /*++ Routine Description: This routine handles receive indications from IPX. Arguments: MacBindingHandle - A handle to use when calling NdisTransferData. MacReceiveContext - A context to use when calling NdisTransferData. RemoteAddress - The local target this packet was received from. MacOptions - The MAC options for the underlying NDIS binding. LookaheadBuffer - The lookahead buffer, starting at the IPX header. LookaheadBufferSize - The length of the lookahead data. LookaheadBufferOffset - The offset to add when calling NdisTransferData. PacketSize - The total length of the packet, starting at the IPX header. Return Value: None. --*/ { PNB_FRAME NbFrame = (PNB_FRAME)LookaheadBuffer; UCHAR DataStreamType; // // We know that this is a frame with a valid IPX header // because IPX would not give it to use otherwise. However, // it does not check the source socket. // if (NbFrame->Connectionless.IpxHeader.SourceSocket != NB_SOCKET) { return; } ++NbiDevice->Statistics.PacketsReceived; // First assume that the DataStreamType is at the normal place i.e 2nd byte // // Now see if this is a name frame. // if ( PacketSize == sizeof(IPX_HEADER) + sizeof(NB_NAME_FRAME) ) { // In the internet mode, the DataStreamType2 becomes DataStreamType if (NbFrame->Connectionless.IpxHeader.PacketType == 0x14 ) { DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType2; } else { DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; } // Is this a name frame? // NB_CMD_FIND_NAME = 1 .... NB_CMD_DELETE_NAME = 5 // if ((DataStreamType >= NB_CMD_FIND_NAME) && (DataStreamType <= NB_CMD_DELETE_NAME)) { if (LookaheadBufferSize == PacketSize) { (*NbiCallbacksNoTransfer[DataStreamType-1])( RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize); } return; } } #ifdef RSRC_TIMEOUT_DBG if ((PacketSize >= sizeof(NB_CONNECTION)) && (NbFrame->Connection.Session.DataStreamType == NB_CMD_DEATH_PACKET)) { NbiProcessDeathPacket( MacBindingHandle, MacReceiveContext, RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize, LookaheadBufferOffset, PacketSize); } #endif //RSRC_TIMEOUT_DBG if ((PacketSize >= sizeof(NB_CONNECTION)) && (NbFrame->Connection.Session.DataStreamType == NB_CMD_SESSION_DATA)) { NbiProcessSessionData( MacBindingHandle, MacReceiveContext, RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize, LookaheadBufferOffset, PacketSize); } else { DataStreamType = NbFrame->Connectionless.NameFrame.DataStreamType; // Handle NB_CMD_SESSION_END = 7 ... NB_CMD_STATUS_QUERY = 9 // if ((DataStreamType >= NB_CMD_SESSION_END ) && (DataStreamType <= NB_CMD_STATUS_QUERY)) { if (LookaheadBufferSize == PacketSize) { (*NbiCallbacksNoTransfer[DataStreamType-1])( RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize); } } else if (DataStreamType == NB_CMD_STATUS_RESPONSE) { NbiProcessStatusResponse( MacBindingHandle, MacReceiveContext, RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize, LookaheadBufferOffset, PacketSize); } else if ((DataStreamType == NB_CMD_DATAGRAM) || (DataStreamType == NB_CMD_BROADCAST_DATAGRAM)) { NbiProcessDatagram( MacBindingHandle, MacReceiveContext, RemoteAddress, MacOptions, LookaheadBuffer, LookaheadBufferSize, LookaheadBufferOffset, PacketSize, (BOOLEAN)(DataStreamType == NB_CMD_BROADCAST_DATAGRAM)); } } } /* NbiReceive */ VOID NbiReceiveComplete( IN USHORT NicId ) /*++ Routine Description: This routine handles receive complete indications from IPX. Arguments: NicId - The NIC ID on which a receive was previously indicated. Return Value: None. --*/ { PLIST_ENTRY p; PADDRESS Address; PREQUEST Request; PNB_RECEIVE_BUFFER ReceiveBuffer; PDEVICE Device = NbiDevice; LIST_ENTRY LocalList; PCONNECTION Connection; NB_DEFINE_LOCK_HANDLE (LockHandle); // // Complete any pending receive requests. // if (!IsListEmpty (&Device->ReceiveCompletionQueue)) { p = NB_REMOVE_HEAD_LIST( &Device->ReceiveCompletionQueue, &Device->Lock); while (!NB_LIST_WAS_EMPTY(&Device->ReceiveCompletionQueue, p)) { Request = LIST_ENTRY_TO_REQUEST (p); // // BUGBUG: Cache the connection somewhere easier // to retrieve? // Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); NbiCompleteRequest (Request); NbiFreeRequest (NbiDevice, Request); Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; NbiDereferenceConnection (Connection, CREF_RECEIVE); p = NB_REMOVE_HEAD_LIST( &Device->ReceiveCompletionQueue, &Device->Lock); } } // // Indicate any datagrams to clients. // if (!IsListEmpty (&Device->ReceiveDatagrams)) { p = NB_REMOVE_HEAD_LIST( &Device->ReceiveDatagrams, &Device->Lock); while (!NB_LIST_WAS_EMPTY(&Device->ReceiveDatagrams, p)) { ReceiveBuffer = CONTAINING_RECORD (p, NB_RECEIVE_BUFFER, WaitLinkage); Address = ReceiveBuffer->Address; NbiIndicateDatagram( Address, ReceiveBuffer->RemoteName, ReceiveBuffer->Data, ReceiveBuffer->DataLength); #if defined(_PNP_POWER) NbiPushReceiveBuffer ( ReceiveBuffer ); #else NB_PUSH_ENTRY_LIST( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage, &Device->Lock); #endif _PNP_POWER NbiDereferenceAddress (Address, AREF_FIND); p = NB_REMOVE_HEAD_LIST( &Device->ReceiveDatagrams, &Device->Lock); } } // // Start packetizing connections. // if (!IsListEmpty (&Device->PacketizeConnections)) { NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle); // // Check again because it may just have become // empty, and the code below depends on it being // non-empty. // if (!IsListEmpty (&Device->PacketizeConnections)) { // // We copy the list locally, in case someone gets // put back on it. We have to hack the end so // it points to LocalList instead of PacketizeConnections. // LocalList = Device->PacketizeConnections; LocalList.Flink->Blink = &LocalList; LocalList.Blink->Flink = &LocalList; InitializeListHead (&Device->PacketizeConnections); // // Set all these connections to not be on the list, so // NbiStopConnection won't try to take them off. // for (p = LocalList.Flink; p != &LocalList; p = p->Flink) { Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); CTEAssert (Connection->OnPacketizeQueue); Connection->OnPacketizeQueue = FALSE; } NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); while (TRUE) { p = RemoveHeadList (&LocalList); if (p == &LocalList) { break; } Connection = CONTAINING_RECORD (p, CONNECTION, PacketizeLinkage); NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); if ((Connection->State == CONNECTION_STATE_ACTIVE) && (Connection->SubState == CONNECTION_SUBSTATE_A_PACKETIZE)) { NbiPacketizeSend( Connection NB_LOCK_HANDLE_ARG (LockHandle) ); } else { NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); } NbiDereferenceConnection (Connection, CREF_PACKETIZE); } } else { NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle); } } } /* NbiReceiveComplete */ VOID NbiTransferDataComplete( IN PNDIS_PACKET Packet, IN NDIS_STATUS Status, IN UINT BytesTransferred ) /*++ Routine Description: This routine handles a transfer data complete indication from IPX, indicating that a previously issued NdisTransferData call has completed. Arguments: Packet - The packet associated with the transfer. Status - The status of the transfer. BytesTransferred - The number of bytes transferred. Return Value: None. --*/ { PNB_RECEIVE_RESERVED ReceiveReserved; PNB_RECEIVE_BUFFER ReceiveBuffer; PADDRESS Address; PCONNECTION Connection; PNDIS_BUFFER CurBuffer, TmpBuffer; PREQUEST AdapterStatusRequest; PDEVICE Device = NbiDevice; CTELockHandle CancelLH; NB_DEFINE_LOCK_HANDLE (LockHandle); ReceiveReserved = (PNB_RECEIVE_RESERVED)(Packet->ProtocolReserved); switch (ReceiveReserved->Type) { case RECEIVE_TYPE_DATA: CTEAssert (ReceiveReserved->TransferInProgress); ReceiveReserved->TransferInProgress = FALSE; Connection = ReceiveReserved->u.RR_CO.Connection; NB_GET_CANCEL_LOCK( &CancelLH ); NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle); if (Status != NDIS_STATUS_SUCCESS) { if (Connection->State == CONNECTION_STATE_ACTIVE) { Connection->CurrentReceive = Connection->PreviousReceive; Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); NB_FREE_CANCEL_LOCK( CancelLH ); // // BUGBUG: Send a resend ack? // } else { // // This aborts the current receive and // releases the connection lock. // NbiCompleteReceive( Connection, ReceiveReserved->u.RR_CO.EndOfMessage, CancelLH NB_LOCK_HANDLE_ARG(LockHandle)); } } else { Connection->CurrentReceive.Offset += BytesTransferred; Connection->CurrentReceive.MessageOffset += BytesTransferred; if (ReceiveReserved->u.RR_CO.CompleteReceive || (Connection->State != CONNECTION_STATE_ACTIVE)) { if (ReceiveReserved->u.RR_CO.EndOfMessage) { CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); ++Connection->ReceiveSequence; ++Connection->LocalRcvSequenceMax; // harmless if NewNetbios is FALSE Connection->CurrentReceive.MessageOffset = 0; Connection->CurrentIndicateOffset = 0; } else if (Connection->NewNetbios) { if (ReceiveReserved->u.RR_CO.PartialReceive) { Connection->CurrentIndicateOffset += BytesTransferred; } else { ++Connection->ReceiveSequence; ++Connection->LocalRcvSequenceMax; Connection->CurrentIndicateOffset = 0; } } // // This sends an ack and releases the connection lock. // NbiCompleteReceive( Connection, ReceiveReserved->u.RR_CO.EndOfMessage, CancelLH NB_LOCK_HANDLE_ARG(LockHandle)); } else { NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); NB_FREE_CANCEL_LOCK( CancelLH ); Connection->ReceiveState = CONNECTION_RECEIVE_ACTIVE; if (Connection->NewNetbios) { // // A partial receive should only happen if we are // completing the receive. // CTEAssert (!ReceiveReserved->u.RR_CO.PartialReceive); ++Connection->ReceiveSequence; ++Connection->LocalRcvSequenceMax; Connection->CurrentIndicateOffset = 0; if ((Connection->CurrentReceiveNoPiggyback) || ((Device->AckWindow != 0) && (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { NbiSendDataAck( Connection, NbiAckResponse NB_LOCK_HANDLE_ARG(LockHandle)); } else { NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); } } else { NbiSendDataAck( Connection, NbiAckResponse NB_LOCK_HANDLE_ARG(LockHandle)); } } } // // Free the NDIS buffer chain if we allocated one. // if (!ReceiveReserved->u.RR_CO.NoNdisBuffer) { NdisQueryPacket (Packet, NULL, NULL, &CurBuffer, NULL); while (CurBuffer) { TmpBuffer = NDIS_BUFFER_LINKAGE (CurBuffer); NdisFreeBuffer (CurBuffer); CurBuffer = TmpBuffer; } } NdisReinitializePacket (Packet); ExInterlockedPushEntrySList( &Device->ReceivePacketList, &ReceiveReserved->PoolLinkage, &NbiGlobalPoolInterlock); NbiDereferenceConnection (Connection, CREF_INDICATE); break; case RECEIVE_TYPE_DATAGRAM: CTEAssert (ReceiveReserved->TransferInProgress); ReceiveReserved->TransferInProgress = FALSE; ReceiveBuffer = ReceiveReserved->u.RR_DG.ReceiveBuffer; // // Free the packet used for the transfer. // ReceiveReserved->u.RR_DG.ReceiveBuffer = NULL; NdisReinitializePacket (Packet); ExInterlockedPushEntrySList( &Device->ReceivePacketList, &ReceiveReserved->PoolLinkage, &NbiGlobalPoolInterlock); // // If it succeeded then queue it for indication, // otherwise free the receive buffer also. // if (Status == STATUS_SUCCESS) { ReceiveBuffer->DataLength = BytesTransferred; NB_INSERT_HEAD_LIST( &Device->ReceiveDatagrams, &ReceiveBuffer->WaitLinkage, &Device->Lock); } else { Address = ReceiveBuffer->Address; #if defined(_PNP_POWER) NbiPushReceiveBuffer ( ReceiveBuffer ); #else NB_PUSH_ENTRY_LIST( &Device->ReceiveBufferList, &ReceiveBuffer->PoolLinkage, &Device->Lock); #endif _PNP_POWER NbiDereferenceAddress (Address, AREF_FIND); } break; case RECEIVE_TYPE_ADAPTER_STATUS: CTEAssert (ReceiveReserved->TransferInProgress); ReceiveReserved->TransferInProgress = FALSE; AdapterStatusRequest = ReceiveReserved->u.RR_AS.Request; // // Free the packet used for the transfer. // NdisReinitializePacket (Packet); ExInterlockedPushEntrySList( &Device->ReceivePacketList, &ReceiveReserved->PoolLinkage, &NbiGlobalPoolInterlock); // // Complete the request. // if (Status == STATUS_SUCCESS) { // // REQUEST_STATUS() is already to set to SUCCESS or // BUFFER_OVERFLOW based on whether the buffer was // big enough. // REQUEST_INFORMATION(AdapterStatusRequest) = BytesTransferred; } else { REQUEST_INFORMATION(AdapterStatusRequest) = 0; REQUEST_STATUS(AdapterStatusRequest) = STATUS_UNEXPECTED_NETWORK_ERROR; } NbiCompleteRequest (AdapterStatusRequest); NbiFreeRequest (Device, AdapterStatusRequest); NbiDereferenceDevice (Device, DREF_STATUS_QUERY); break; } } /* NbiTransferDataComplete */ VOID NbiAcknowledgeReceive( IN PCONNECTION Connection IN NB_LOCK_HANDLE_PARAM(LockHandle) ) /*++ Routine Description: This routine is called when a receive needs to be acked to the remote. It either sends a data ack or queues up a piggyback ack request. NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD AND RETURNS WITH IT RELEASED. Arguments: Connection - Pointer to the connection. LockHandle - The handle with which Connection->Lock was acquired. Return Value: None. --*/ { PDEVICE Device = NbiDevice; if (Connection->NewNetbios) { // // CurrentReceiveNoPiggyback is based on the bits he // set in his frame, NoPiggybackHeuristic is based on // guesses about the traffic pattern, it is set to // TRUE if we think we should not piggyback. // if ((!Device->EnablePiggyBackAck) || (Connection->CurrentReceiveNoPiggyback) || (Connection->PiggybackAckTimeout) || (Connection->NoPiggybackHeuristic)) { // // This releases the lock. // NbiSendDataAck( Connection, NbiAckResponse NB_LOCK_HANDLE_ARG(LockHandle)); } else { if (!Connection->DataAckPending) { NB_DEFINE_LOCK_HANDLE (LockHandle1) // // Some stacks can have multiple messages // outstanding, so we may already have an // ack queued. // Connection->DataAckTimeouts = 0; Connection->DataAckPending = TRUE; ++Device->Statistics.PiggybackAckQueued; if (!Connection->OnDataAckQueue) { NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle1); if (!Connection->OnDataAckQueue) { Connection->OnDataAckQueue = TRUE; InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage); } if (!Device->DataAckActive) { NbiStartShortTimer (Device); Device->DataAckActive = TRUE; } NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle1); } // // Clear this, since a message ack resets the count. // Connection->ReceivesWithoutAck = 0; } NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); } } else { // // This releases the lock. // NbiSendDataAck( Connection, NbiAckResponse NB_LOCK_HANDLE_ARG(LockHandle)); } } VOID NbiCompleteReceive( IN PCONNECTION Connection, IN BOOLEAN EndOfMessage, IN CTELockHandle CancelLH IN NB_LOCK_HANDLE_PARAM(LockHandle) ) /*++ Routine Description: This routine is called when we have filled up a receive request and need to complete it. NOTE: THIS FUNCTION IS CALLED WITH THE CONNECTION LOCK HELD AND RETURNS WITH IT RELEASED. THIS ROUTINE ALSO HOLDS CANCEL SPIN LOCK WHEN IT IS CALLED AND RELEASES IT WHEN IT RETURNS. Arguments: Connection - Pointer to the connection. EndOfMessage - BOOLEAN set to true if the message end was received. LockHandle - The handle with which Connection->Lock was acquired. Return Value: None. --*/ { PREQUEST Request; PDEVICE Device = NbiDevice; // // Complete the current receive request. If the connection // has shut down then we complete it right here, otherwise // we queue it for completion in the receive complete // handler. // Request = Connection->ReceiveRequest; IoSetCancelRoutine (Request, (PDRIVER_CANCEL)NULL); NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); NB_FREE_CANCEL_LOCK( CancelLH ); if (Connection->State != CONNECTION_STATE_ACTIVE) { Connection->ReceiveRequest = NULL; // StopConnection won't do this REQUEST_STATUS(Request) = Connection->Status; NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); NB_DEBUG2 (RECEIVE, ("Completing receive %lx (%d), status %lx\n", Request, REQUEST_INFORMATION(Request), REQUEST_STATUS(Request))); NbiCompleteRequest (Request); NbiFreeRequest (NbiDevice, Request); ++Connection->ConnectionInfo.ReceiveErrors; NbiDereferenceConnection (Connection, CREF_RECEIVE); } else { REQUEST_INFORMATION (Request) = Connection->CurrentReceive.Offset; if (EndOfMessage) { REQUEST_STATUS(Request) = STATUS_SUCCESS; } else { REQUEST_STATUS(Request) = STATUS_BUFFER_OVERFLOW; } // // If we indicated to the client, adjust this down by the // amount of data taken, when it hits zero we can reindicate. // if (Connection->ReceiveUnaccepted) { NB_DEBUG2 (RECEIVE, ("Moving Unaccepted %d down by %d\n", Connection->ReceiveUnaccepted, Connection->CurrentReceive.Offset)); if (Connection->CurrentReceive.Offset >= Connection->ReceiveUnaccepted) { Connection->ReceiveUnaccepted = 0; } else { Connection->ReceiveUnaccepted -= Connection->CurrentReceive.Offset; } } // // BUGBUG: Check whether to activate another receive? // Connection->ReceiveState = CONNECTION_RECEIVE_PENDING; Connection->ReceiveRequest = NULL; // // This releases the lock. // if (Connection->NewNetbios) { if (EndOfMessage) { NbiAcknowledgeReceive( Connection NB_LOCK_HANDLE_ARG(LockHandle)); } else { if (Connection->CurrentIndicateOffset != 0) { NbiSendDataAck( Connection, NbiAckResend NB_LOCK_HANDLE_ARG(LockHandle)); } else if ((Connection->CurrentReceiveNoPiggyback) || ((Device->AckWindow != 0) && (++Connection->ReceivesWithoutAck >= Device->AckWindow))) { NbiSendDataAck( Connection, NbiAckResponse NB_LOCK_HANDLE_ARG(LockHandle)); } else { NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); } } } else { NbiSendDataAck( Connection, EndOfMessage ? NbiAckResponse : NbiAckResend NB_LOCK_HANDLE_ARG(LockHandle)); } ++Connection->ConnectionInfo.ReceivedTsdus; // // This will complete the request inside ReceiveComplete, // dereference the connection, and set the state to IDLE. // NB_INSERT_TAIL_LIST( &Device->ReceiveCompletionQueue, REQUEST_LINKAGE (Request), &Device->Lock); } } /* NbiCompleteReceive */ NTSTATUS NbiTdiReceive( IN PDEVICE Device, IN PREQUEST Request ) /*++ Routine Description: This routine does a receive on an active connection. Arguments: Device - The netbios device. Request - The request describing the receive. Return Value: NTSTATUS - status of operation. --*/ { PCONNECTION Connection; 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) { IoSetCancelRoutine (Request, NbiCancelReceive); NB_SYNC_SWAP_IRQL( CancelLH, LockHandle ); NB_FREE_CANCEL_LOCK( CancelLH ); NbiReferenceConnectionSync (Connection, CREF_RECEIVE); // // Insert this in our queue, then see if we need // to wake up the remote. // REQUEST_SINGLE_LINKAGE(Request) = NULL; REQUEST_LIST_INSERT_TAIL(&Connection->ReceiveQueue, Request); if (Connection->ReceiveState != CONNECTION_RECEIVE_W_RCV) { NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx idle\n", Request, Connection)); NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle); } else { NB_DEBUG2 (RECEIVE, ("Receive %lx, connection %lx awakened\n", Request, Connection)); Connection->ReceiveState = CONNECTION_RECEIVE_IDLE; // // This releases the lock. // if (Connection->NewNetbios) { Connection->LocalRcvSequenceMax = (USHORT) (Connection->ReceiveSequence + Connection->ReceiveWindowSize - 1); } NbiSendDataAck( Connection, NbiAckResend NB_LOCK_HANDLE_ARG(LockHandle)); } NB_END_SYNC (&SyncContext); return STATUS_PENDING; } else { NB_DEBUG2 (RECEIVE, ("Receive %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_DEBUG2 (RECEIVE, ("Receive 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 (RECEIVE, ("Receive connection %lx has bad signature\n", Connection)); return STATUS_INVALID_CONNECTION; } } /* NbiTdiReceive */ VOID NbiCancelReceive( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to cancel a receive. The request is found on the connection's receive 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_RECEIVE)); CTEAssert (REQUEST_OPEN_TYPE(Request) == (PVOID)TDI_CONNECTION_FILE); Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request); // // Just stop the connection, that will tear down any // receives. // // BUGBUG: Do we care about cancelling non-active // receives without stopping the connection?? // // BUGBUG: This routine is the same as NbiCancelSend, // so if we don't make it more specific, merge the two. // 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); } /* NbiCancelReceive */