You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1314 lines
35 KiB
1314 lines
35 KiB
/*++
|
|
|
|
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
|
|
|
|
|
|
BOOLEAN
|
|
NbiReceive(
|
|
IN NDIS_HANDLE MacBindingHandle,
|
|
IN NDIS_HANDLE MacReceiveContext,
|
|
IN ULONG_PTR FwdAdapterCtx,
|
|
IN PIPX_LOCAL_TARGET RemoteAddress,
|
|
IN ULONG MacOptions,
|
|
IN PUCHAR LookaheadBuffer,
|
|
IN UINT LookaheadBufferSize,
|
|
IN UINT LookaheadBufferOffset,
|
|
IN UINT PacketSize,
|
|
IN PMDL pMdl
|
|
)
|
|
|
|
/*++
|
|
|
|
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:
|
|
|
|
TRUE - receivepacket taken, will return later with NdisReturnPacket.
|
|
Currently, we always return FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
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 FALSE;
|
|
}
|
|
|
|
++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 FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
#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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
} /* 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);
|
|
|
|
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 );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
if ( Request->Type != 0x6 ) {
|
|
DbgPrint("NB: tracking pool corruption in rcv irp %lx \n", Request );
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
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;
|
|
|
|
//
|
|
// Check that the file type is valid
|
|
//
|
|
if (REQUEST_OPEN_TYPE(Request) != (PVOID)TDI_CONNECTION_FILE)
|
|
{
|
|
CTEAssert(FALSE);
|
|
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
|
}
|
|
|
|
//
|
|
// First make sure the connection is valid.
|
|
//
|
|
Connection = (PCONNECTION)REQUEST_OPEN_CONTEXT(Request);
|
|
if (Connection->Type == NB_CONNECTION_SIGNATURE) {
|
|
|
|
NB_GET_CANCEL_LOCK( &CancelLH );
|
|
NB_BEGIN_SYNC (&SyncContext);
|
|
NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
|
|
|
|
//
|
|
// Make sure the connection is in a good state.
|
|
//
|
|
|
|
if (Connection->State == CONNECTION_STATE_ACTIVE) {
|
|
|
|
//
|
|
// If the connection is idle then send it now, otherwise
|
|
// queue it.
|
|
//
|
|
|
|
|
|
if (!Request->Cancel) {
|
|
|
|
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.
|
|
//
|
|
// Do we care about cancelling non-active
|
|
// receives without stopping the connection??
|
|
//
|
|
// 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 */
|
|
|