mirror of https://github.com/lianthony/NT4.0
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.
1505 lines
42 KiB
1505 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sendeng.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code that implements the send engine for the
|
|
Sample transport provider.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "st.h"
|
|
#if 27
|
|
ULONG StNoisySend = 0;
|
|
ULONG StSndLoc = 0;
|
|
ULONG StSnds[10];
|
|
#endif
|
|
|
|
|
|
VOID
|
|
StartPacketizingConnection(
|
|
PTP_CONNECTION Connection,
|
|
IN BOOLEAN Immediate,
|
|
IN KIRQL ConnectionIrql,
|
|
IN KIRQL CancelIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to place a connection on the PacketizeQueue
|
|
of its device context object. Then this routine starts packetizing
|
|
the first connection on that queue.
|
|
|
|
*** The Connection spin lock must be held on entry to this routine.
|
|
Optionally, the cancel spin lock may be held. If so, the cancel
|
|
spin lock must have been acquired first.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Immediate - TRUE if the connection should be packetized
|
|
immediately; FALSE if the connection should be queued
|
|
up for later packetizing (implies that ReceiveComplete
|
|
will be called in the future, which packetizes always).
|
|
|
|
NOTE: If this is TRUE, it also implies that we have
|
|
a connection reference.
|
|
|
|
ConnectionIrql - The OldIrql value when the connection spin lock
|
|
was acquired.
|
|
|
|
CancelIrql - The OldIrql value when the cancel spin lock was
|
|
acquired. -1 means that the cancel spin lock isn't held.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
|
|
DeviceContext = Connection->Provider;
|
|
|
|
//
|
|
// If this connection's SendState is set to PACKETIZE and if
|
|
// we are not already on the PacketizeQueue, then go ahead and
|
|
// append us to the end of that queue, and remember that we're
|
|
// on it by setting the CONNECTION_FLAGS_PACKETIZE bitflag.
|
|
//
|
|
// Also don't queue it if the connection is stopping.
|
|
//
|
|
|
|
if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) &&
|
|
!(Connection->Flags &
|
|
(CONNECTION_FLAGS_PACKETIZE | CONNECTION_FLAGS_STOPPING))) {
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
if (!Immediate) {
|
|
StReferenceConnection ("Packetize", Connection);
|
|
}
|
|
|
|
ExInterlockedInsertTailList(
|
|
&DeviceContext->PacketizeQueue,
|
|
&Connection->PacketizeLinkage,
|
|
&DeviceContext->SpinLock);
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql);
|
|
|
|
} else {
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql);
|
|
if (Immediate) {
|
|
StDereferenceConnection("temp TdiSend", Connection);
|
|
}
|
|
}
|
|
|
|
if (CancelIrql != (KIRQL)-1) {
|
|
IoReleaseCancelSpinLock (CancelIrql);
|
|
}
|
|
|
|
if (Immediate) {
|
|
PacketizeConnections (DeviceContext);
|
|
}
|
|
|
|
} /* StartPacketizingConnection */
|
|
|
|
|
|
VOID
|
|
PacketizeConnections(
|
|
PDEVICE_CONTEXT DeviceContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to packetize all connections waiting on the
|
|
PacketizeQueue of the DeviceContext.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceContext - Pointer to a DEVICE_CONTEXT object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY p;
|
|
PTP_CONNECTION Connection;
|
|
|
|
//
|
|
// Pick connections off of the device context's packetization queue
|
|
// until there are no more left to pick off. For each one, we call
|
|
// PacketizeSend. Note this routine can be executed concurrently
|
|
// on multiple processors and it doesn't matter; multiple connections
|
|
// may be packetized concurrently.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
p = ExInterlockedRemoveHeadList(
|
|
&DeviceContext->PacketizeQueue,
|
|
&DeviceContext->SpinLock);
|
|
|
|
if (p == NULL) {
|
|
break;
|
|
}
|
|
Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage);
|
|
PacketizeSend (Connection);
|
|
}
|
|
|
|
} /* PacketizeConnections */
|
|
|
|
|
|
VOID
|
|
PacketizeSend(
|
|
PTP_CONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packetizes the current TdiSend request on the specified
|
|
connection as much as limits will permit. A given here is that there
|
|
is an active send on the connection that needs further packetization.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, oldirql1;
|
|
ULONG MaxFrameSize, FrameSize;
|
|
ULONG PacketBytes;
|
|
TP_SEND_POINTER SavedSendPointer;
|
|
PNDIS_BUFFER PacketDescriptor;
|
|
PUCHAR SourceRouting;
|
|
UINT SourceRoutingLength;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PTP_PACKET Packet;
|
|
NTSTATUS Status;
|
|
PST_HEADER StHeader;
|
|
UINT HeaderLength;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PSEND_PACKET_TAG SendTag;
|
|
ULONG LastPacketLength;
|
|
|
|
DeviceContext = Connection->Provider;
|
|
|
|
//
|
|
// Just loop until one of three events happens: (1) we run out of
|
|
// packets from StCreatePacket, (2) we completely packetize the send.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) {
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
StDereferenceConnection ("No longer packetizing", Connection);
|
|
return;
|
|
}
|
|
|
|
MaxFrameSize = Connection->MaximumDataSize;
|
|
|
|
//
|
|
// It is possible for a frame to arrive during the middle of this loop
|
|
// (such as a NO_RECEIVE) that will put us into a new state (such as
|
|
// W_RCVCONT). For this reason, we have to check the state every time
|
|
// (at the end of the loop).
|
|
//
|
|
|
|
do {
|
|
|
|
if (!NT_SUCCESS (StCreatePacket (DeviceContext, &Packet))) {
|
|
|
|
//
|
|
// We need a packet to finish packetizing the current send, but
|
|
// there are no more packets available in the pool right now.
|
|
// Set our send state to W_PACKET, and put this connection on
|
|
// the PacketWaitQueue of the device context object. Then,
|
|
// when StDestroyPacket frees up a packet, it will check this
|
|
// queue for starved connections, and if it finds one, it will
|
|
// take a connection off the list and set its send state to
|
|
// SENDSTATE_PACKETIZE and put it on the PacketizeQueue.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_PACKET;
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer
|
|
// on the PacketizeQueue or actively packetizing. The flag
|
|
// was set by StartPacketizingConnection to indicate that
|
|
// the connection was already on the PacketizeQueue.
|
|
//
|
|
// Don't queue him if the connection is stopping.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) {
|
|
Connection->Flags |= CONNECTION_FLAGS_SUSPENDED;
|
|
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1);
|
|
InsertTailList (&DeviceContext->PacketWaitQueue, &Connection->PacketWaitLinkage);
|
|
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
StDereferenceConnection ("No packet", Connection);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Add a reference count to the IRP, and keep track of
|
|
// which request it is. Send completion will remove the
|
|
// reference.
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp);
|
|
|
|
SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved);
|
|
SendTag->Type = TYPE_I_FRAME;
|
|
SendTag->Packet = Packet;
|
|
SendTag->Owner = (PVOID)IrpSp;
|
|
|
|
Packet->CompleteSend = FALSE;
|
|
|
|
|
|
//
|
|
// Build the MAC header. All frames go out as
|
|
// single-route source routing.
|
|
//
|
|
|
|
|
|
MacReturnSingleRouteSR(
|
|
&DeviceContext->MacInfo,
|
|
&SourceRouting,
|
|
&SourceRoutingLength);
|
|
|
|
MacConstructHeader (
|
|
&DeviceContext->MacInfo,
|
|
Packet->Header,
|
|
DeviceContext->MulticastAddress.Address,
|
|
DeviceContext->LocalAddress.Address,
|
|
sizeof(ST_HEADER),
|
|
SourceRouting,
|
|
SourceRoutingLength,
|
|
&HeaderLength);
|
|
|
|
//
|
|
// Build the header: 'I', dest, source
|
|
//
|
|
|
|
StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]);
|
|
|
|
StHeader->Signature = ST_SIGNATURE;
|
|
StHeader->Command = ST_CMD_INFORMATION;
|
|
StHeader->Flags = 0;
|
|
|
|
RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16);
|
|
RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16);
|
|
|
|
HeaderLength += sizeof(ST_HEADER);
|
|
|
|
|
|
//
|
|
// Modify the packet length and send the it.
|
|
//
|
|
|
|
StSetNdisPacketLength(Packet->NdisPacket, HeaderLength);
|
|
|
|
StReferenceSendIrp ("Packetize", IrpSp);
|
|
|
|
//
|
|
// Save our complex send pointer in case we have to restore it
|
|
// because StNdisSend fails.
|
|
//
|
|
|
|
SavedSendPointer = Connection->sp;
|
|
|
|
//
|
|
// build an NDIS_BUFFER chain that describes the buffer we're using, and
|
|
// thread it off the NdisBuffer. This chain may not complete the
|
|
// packet, as the remaining part of the MDL chain may be shorter than
|
|
// the packet.
|
|
//
|
|
|
|
FrameSize = MaxFrameSize;
|
|
|
|
//
|
|
// Check if we have less than FrameSize left to send.
|
|
//
|
|
|
|
if (Connection->sp.MessageBytesSent + FrameSize > Connection->CurrentSendLength) {
|
|
|
|
FrameSize = Connection->CurrentSendLength - Connection->sp.MessageBytesSent;
|
|
|
|
}
|
|
#if 27
|
|
if (StNoisySend) {
|
|
DbgPrint ("Send %d of %d\n", FrameSize, Connection->CurrentSendLength);
|
|
}
|
|
if (FrameSize > 1000) {
|
|
StSnds[StSndLoc] = FrameSize;
|
|
StSndLoc = (StSndLoc + 1) % 10;
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// Make a copy of the MDL chain for this send, unless
|
|
// there are zero bytes left.
|
|
//
|
|
|
|
if (FrameSize != 0) {
|
|
|
|
//
|
|
// If the whole send will fit inside one packet,
|
|
// then there is no need to duplicate the MDL
|
|
// (note that this may include multi-MDL sends).
|
|
//
|
|
|
|
if ((Connection->sp.SendByteOffset == 0) &&
|
|
(Connection->CurrentSendLength == FrameSize)) {
|
|
|
|
PacketDescriptor = (PNDIS_BUFFER)Connection->sp.CurrentSendMdl;
|
|
PacketBytes = FrameSize;
|
|
Connection->sp.CurrentSendMdl = NULL;
|
|
Connection->sp.SendByteOffset = FrameSize;
|
|
Packet->PacketNoNdisBuffer = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
Status = BuildBufferChainFromMdlChain (
|
|
DeviceContext->NdisBufferPoolHandle,
|
|
Connection->sp.CurrentSendMdl,
|
|
Connection->sp.SendByteOffset,
|
|
FrameSize,
|
|
&PacketDescriptor,
|
|
&Connection->sp.CurrentSendMdl,
|
|
&Connection->sp.SendByteOffset,
|
|
&PacketBytes);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
PacketBytes = 0;
|
|
Connection->sp.CurrentSendMdl = NULL;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
Connection->sp.MessageBytesSent += PacketBytes;
|
|
|
|
//
|
|
// Chain the buffers to the packet, unless there
|
|
// are zero bytes of data.
|
|
//
|
|
|
|
if (FrameSize != 0) {
|
|
NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor);
|
|
}
|
|
|
|
|
|
//
|
|
// Have we run out of Mdl Chain in this request?
|
|
//
|
|
|
|
if ((PacketBytes < FrameSize) ||
|
|
(Connection->sp.CurrentSendMdl == NULL) ||
|
|
(Connection->CurrentSendLength <= Connection->sp.MessageBytesSent)) {
|
|
|
|
//
|
|
// Yep. We know that we've exhausted the current request's buffer
|
|
// here, so see if there's another request without EOF set that we
|
|
// can build start throwing into this packet.
|
|
//
|
|
|
|
|
|
if (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) {
|
|
|
|
//
|
|
// We are sending the last packet in a message. Change
|
|
// the packet type to a "last" frame.
|
|
//
|
|
|
|
StHeader->Flags |= ST_FLAGS_LAST;
|
|
Packet->CompleteSend = TRUE;
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We are sending the last packet in this request. If there
|
|
// are more requests in the connection's SendQueue, then
|
|
// advance complex send pointer to point to the next one
|
|
// in line. Otherwise, if there aren't any more requests
|
|
// ready to packetize, then we enter the W_EOR state and
|
|
// stop packetizing. Note that we're waiting here for the TDI
|
|
// client to come up with data to send; we're just hanging out
|
|
// until then.
|
|
//
|
|
|
|
if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) {
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_EOR;
|
|
|
|
} else {
|
|
|
|
Connection->sp.CurrentSendIrp =
|
|
CONTAINING_RECORD (
|
|
Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
Connection->sp.CurrentSendMdl =
|
|
Connection->sp.CurrentSendIrp->MdlAddress;
|
|
Connection->sp.SendByteOffset = 0;
|
|
Connection->CurrentSendLength +=
|
|
IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp));
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
LastPacketLength = sizeof(ST_HEADER) + PacketBytes;
|
|
|
|
MacModifyHeader(
|
|
&DeviceContext->MacInfo,
|
|
Packet->Header,
|
|
LastPacketLength);
|
|
|
|
Packet->NdisIFrameLength = LastPacketLength;
|
|
|
|
StNdisSend (Packet);
|
|
|
|
//
|
|
// Update our counters (this is done unprotected by a lock).
|
|
//
|
|
|
|
ADD_TO_LARGE_INTEGER(
|
|
&DeviceContext->IFrameBytesSent,
|
|
PacketBytes);
|
|
++DeviceContext->IFramesSent;
|
|
|
|
} else {
|
|
|
|
//
|
|
// BuildBufferChainFromMdlChain failed; we need to
|
|
// release the lock since the long if() above
|
|
// exits with it released.
|
|
//
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
}
|
|
|
|
//
|
|
// Note that we may have fallen out of the BuildBuffer... if above with
|
|
// Status set to STATUS_INSUFFICIENT_RESOURCES. if we have, we'll just
|
|
// stick this connection back onto the packetize queue and hope the
|
|
// system gets more resources later.
|
|
//
|
|
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
//
|
|
// Restore old complex send pointer.
|
|
//
|
|
|
|
Connection->sp = SavedSendPointer;
|
|
|
|
|
|
//
|
|
// Indicate we're waiting on favorable link conditions.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_W_LINK;
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer
|
|
// on the PacketizeQueue or actively packetizing. The flag
|
|
// was set by StartPacketizingConnection to indicate that
|
|
// the connection was already on the PacketizeQueue.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
StDestroyPacket (Packet);
|
|
StDereferenceSendIrp("Send failed", IrpSp);
|
|
StDereferenceConnection ("Send failed", Connection);
|
|
|
|
return;
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
//
|
|
// It is probable that a frame arrived while we released
|
|
// the connection's spin lock, so our state has probably changed.
|
|
// When we cycle around this loop again, we will have the lock
|
|
// again, so we can test the connection's send state.
|
|
//
|
|
|
|
} while (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE);
|
|
|
|
//
|
|
// Clear the PACKETIZE flag, indicating that we're no longer on the
|
|
// PacketizeQueue or actively packetizing. The flag was set by
|
|
// StartPacketizingConnection to indicate that the connection was
|
|
// already on the PacketizeQueue.
|
|
//
|
|
|
|
Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
StDereferenceConnection ("PacketizeSend done", Connection);
|
|
|
|
} /* PacketizeSend */
|
|
|
|
|
|
VOID
|
|
CompleteSend(
|
|
PTP_CONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to complete a TDI send back to the
|
|
caller. In the sample transport we assume that all sends
|
|
complete successfully, but in other transports we would
|
|
wait for a response from the remote.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, cancelirql;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN EndOfRecord;
|
|
|
|
//
|
|
// Pick off TP_REQUEST objects from the connection's SendQueue until
|
|
// we find one with an END_OF_RECORD mark embedded in it.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// We know for a fact that we wouldn't be calling this routine if
|
|
// we hadn't completed sending an entire message, since we
|
|
// only set the ST_LAST bit in that case. Therefore, we
|
|
// know that we will run into a request with the END_OF_RECORD
|
|
// mark set BEFORE we will run out of requests on that queue,
|
|
// so there is no reason to check to see if we ran off the end.
|
|
// Note that it's possible that the send has been failed and the
|
|
// connection not yet torn down; if this has happened, we could be
|
|
// removing from an empty queue here. Make sure that doesn't happen.
|
|
//
|
|
|
|
if (Connection->SendQueue.Flink == &Connection->SendQueue) {
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
//
|
|
// no requests to complete, things must have failed; just get out.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL);
|
|
|
|
Irp->CancelRoutine = (PDRIVER_CANCEL)NULL;
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
//
|
|
// Complete the send. Note that this may not actually call
|
|
// IoCompleteRequest for the Irp until sometime later, if the
|
|
// in-progress LLC resending going on below us needs to complete.
|
|
//
|
|
|
|
StCompleteSendIrp (
|
|
Irp,
|
|
STATUS_SUCCESS,
|
|
IRP_SEND_LENGTH(IrpSp));
|
|
|
|
if (EndOfRecord) {
|
|
break;
|
|
}
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
};
|
|
|
|
//
|
|
// Acquire the lock that we will return with.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
//
|
|
// We've finished processing the current send. Update our state.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
|
|
//
|
|
// If there is another send pending on the connection, then initialize
|
|
// it and start packetizing it.
|
|
//
|
|
|
|
if (!(IsListEmpty (&Connection->SendQueue))) {
|
|
|
|
InitializeSend (Connection);
|
|
|
|
//
|
|
// This code is similar to calling StartPacketizingConnection
|
|
// with the second parameter FALSE.
|
|
//
|
|
|
|
if ((!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) &&
|
|
(!(Connection->Flags & CONNECTION_FLAGS_STOPPING))) {
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
StReferenceConnection ("Packetize", Connection);
|
|
|
|
ExInterlockedInsertTailList(
|
|
&Connection->Provider->PacketizeQueue,
|
|
&Connection->PacketizeLinkage,
|
|
&Connection->Provider->SpinLock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
} /* CompleteSend */
|
|
|
|
|
|
VOID
|
|
FailSend(
|
|
IN PTP_CONNECTION Connection,
|
|
IN NTSTATUS RequestStatus,
|
|
IN BOOLEAN StopConnection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called because something on the link caused this send to be
|
|
unable to complete. There are a number of possible reasons for this to have
|
|
happened, but all will fail with the common error STATUS_LINK_FAILED.
|
|
or NO_RECEIVE response where the number of bytes specified exactly
|
|
Here we retire all of the TdiSends on the connection's SendQueue up to
|
|
and including the current one, which is the one that failed.
|
|
|
|
Later - Actually, a send failing is cause for the entire circuit to wave
|
|
goodbye to this life. We now simply tear down the connection completly.
|
|
Any future sends on this connection will be blown away.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, cancelirql;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN EndOfRecord;
|
|
BOOLEAN GotCurrent = FALSE;
|
|
|
|
|
|
//
|
|
// Pick off IRP objects from the connection's SendQueue until
|
|
// we get to this one. If this one does NOT have an EOF mark set, we'll
|
|
// need to keep going until we hit one that does have EOF set. Note that
|
|
// this may cause us to continue failing sends that have not yet been
|
|
// queued. (We do all this because ST does not provide stream mode sends.)
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
StReferenceConnection ("Failing Send", Connection);
|
|
|
|
do {
|
|
if (IsListEmpty (&Connection->SendQueue)) {
|
|
|
|
//
|
|
// got an empty list, so we've run out of send requests to fail
|
|
// without running into an EOR. Set the connection flag that will
|
|
// cause all further sends to be failed up to an EOR and get out
|
|
// of here.
|
|
//
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_FAILING_TO_EOR;
|
|
break;
|
|
}
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
if (Irp == Connection->sp.CurrentSendIrp) {
|
|
GotCurrent = TRUE;
|
|
}
|
|
EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL);
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
Irp->CancelRoutine = (PDRIVER_CANCEL)NULL;
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
StCompleteSendIrp (Irp, RequestStatus, 0);
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
} while (!EndOfRecord & !GotCurrent);
|
|
|
|
//
|
|
// We've finished processing the current send. Update our state.
|
|
//
|
|
|
|
Connection->SendState = CONNECTION_SENDSTATE_IDLE;
|
|
Connection->sp.CurrentSendIrp = NULL;
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
|
|
if (StopConnection) {
|
|
StStopConnection (Connection, STATUS_LINK_FAILED);
|
|
}
|
|
|
|
StDereferenceConnection ("FailSend", Connection);
|
|
|
|
} /* FailSend */
|
|
|
|
|
|
VOID
|
|
InitializeSend(
|
|
PTP_CONNECTION Connection
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever the next send on a connection should
|
|
be initialized; that is, all of the fields associated with the state
|
|
of the current send are set to refer to the first send on the SendQueue.
|
|
|
|
WARNING: This routine is executed with the Connection lock acquired
|
|
since it must be atomically executed with the caller's setup.
|
|
|
|
Arguments:
|
|
|
|
Connection - Pointer to a TP_CONNECTION object.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (Connection->SendQueue.Flink != &Connection->SendQueue) {
|
|
Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
|
Connection->FirstSendIrp =
|
|
CONTAINING_RECORD (Connection->SendQueue.Flink, IRP, Tail.Overlay.ListEntry);
|
|
Connection->FirstSendMdl = Connection->FirstSendIrp->MdlAddress;
|
|
Connection->FirstSendByteOffset = 0;
|
|
Connection->sp.MessageBytesSent = 0;
|
|
Connection->sp.CurrentSendIrp = Connection->FirstSendIrp;
|
|
Connection->sp.CurrentSendMdl = Connection->FirstSendMdl;
|
|
Connection->sp.SendByteOffset = Connection->FirstSendByteOffset;
|
|
Connection->CurrentSendLength =
|
|
IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp));
|
|
|
|
}
|
|
} /* InitializeSend */
|
|
|
|
|
|
VOID
|
|
StCancelSend(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to cancel a send.
|
|
The send is found on the connection's send queue; if it is the
|
|
current request it is cancelled and the connection is torn down,
|
|
otherwise it is silently cancelled.
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PTP_CONNECTION Connection;
|
|
PIRP SendIrp;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN Found;
|
|
|
|
UNREFERENCED_PARAMETER (DeviceObject);
|
|
|
|
//
|
|
// Get a pointer to the current stack location in the IRP. This is where
|
|
// the function codes and parameters are stored.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
|
(IrpSp->MinorFunction == TDI_SEND));
|
|
|
|
Connection = IrpSp->FileObject->FsContext;
|
|
|
|
//
|
|
// Since this IRP is still in the cancellable state, we know
|
|
// that the connection is still around (although it may be in
|
|
// the process of being torn down).
|
|
//
|
|
|
|
//
|
|
// See if this is the IRP for the current send request.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
StReferenceConnection ("Cancelling Send", Connection);
|
|
|
|
p = Connection->SendQueue.Flink;
|
|
SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
|
|
if (SendIrp == Irp) {
|
|
|
|
//
|
|
// yes, it is the first one on the send queue, so
|
|
// trash the send/connection.
|
|
//
|
|
|
|
p = RemoveHeadList (&Connection->SendQueue);
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0);
|
|
|
|
//
|
|
// Since we are cancelling the current send, blow away
|
|
// the connection.
|
|
//
|
|
|
|
StStopConnection (Connection, STATUS_CANCELLED);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Scan through the list, looking for this IRP.
|
|
//
|
|
|
|
Found = FALSE;
|
|
p = p->Flink;
|
|
while (p != &Connection->SendQueue) {
|
|
|
|
SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry);
|
|
if (SendIrp == Irp) {
|
|
|
|
//
|
|
// Found it, remove it from the list here.
|
|
//
|
|
|
|
RemoveEntryList (p);
|
|
|
|
Found = TRUE;
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
|
|
//
|
|
// The following dereference will complete the I/O, provided removes
|
|
// the last reference on the request object. The I/O will complete
|
|
// with the status and information stored in the Irp. Therefore,
|
|
// we set those values here before the dereference.
|
|
//
|
|
|
|
StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0);
|
|
break;
|
|
|
|
}
|
|
|
|
p = p->Flink;
|
|
|
|
}
|
|
|
|
if (!Found) {
|
|
|
|
//
|
|
// We didn't find it!
|
|
//
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
|
}
|
|
|
|
}
|
|
|
|
StDereferenceConnection ("Cancelling Send", Connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
StSendCompletionHandler(
|
|
IN NDIS_HANDLE ProtocolBindingContext,
|
|
IN PNDIS_PACKET NdisPacket,
|
|
IN NDIS_STATUS NdisStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O system to indicate that a connection-
|
|
oriented packet has been shipped and is no longer needed by the Physical
|
|
Provider.
|
|
|
|
Arguments:
|
|
|
|
NdisContext - the value associated with the adapter binding at adapter
|
|
open time (which adapter we're talking on).
|
|
|
|
NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent.
|
|
|
|
NdisStatus - the completion status of the send.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSEND_PACKET_TAG SendContext;
|
|
PTP_PACKET Packet;
|
|
KIRQL oldirql, cancelirql;
|
|
PDEVICE_CONTEXT DeviceContext;
|
|
PTP_CONNECTION Connection;
|
|
PLIST_ENTRY p;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PTP_REQUEST request;
|
|
TA_NETBIOS_ADDRESS TempAddress;
|
|
ULONG returnLength;
|
|
NTSTATUS status;
|
|
PTDI_CONNECTION_INFORMATION remoteInformation;
|
|
|
|
UNREFERENCED_PARAMETER(ProtocolBindingContext);
|
|
|
|
SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0];
|
|
Packet = SendContext->Packet;
|
|
|
|
DeviceContext = Packet->Provider;
|
|
|
|
Packet->PacketSent = TRUE;
|
|
|
|
switch (SendContext->Type) {
|
|
|
|
case TYPE_I_FRAME:
|
|
|
|
//
|
|
// Dereference the IRP that this packet was sent for.
|
|
//
|
|
|
|
IrpSp = (PIO_STACK_LOCATION)(SendContext->Owner);
|
|
|
|
if (Packet->CompleteSend) {
|
|
CompleteSend(IRP_CONNECTION(IrpSp));
|
|
}
|
|
StDereferenceSendIrp("Destroy packet", IrpSp);
|
|
break;
|
|
|
|
case TYPE_D_FRAME:
|
|
|
|
//
|
|
// Finish tearing down the connection.
|
|
//
|
|
|
|
StDereferenceConnection("Disconnect completed", (PTP_CONNECTION)(SendContext->Owner));
|
|
break;
|
|
|
|
case TYPE_G_FRAME:
|
|
|
|
//
|
|
// Addresses get their own frames; let the address know it's ok to
|
|
// use the frame again, and exit to avoid normal packet completion.
|
|
//
|
|
|
|
StSendDatagramCompletion ((PTP_ADDRESS)(SendContext->Owner),
|
|
NdisPacket,
|
|
NdisStatus);
|
|
return;
|
|
|
|
case TYPE_C_FRAME:
|
|
|
|
//
|
|
// Complete the TdiConnect request; note that he better
|
|
// have accepted it quickly since we will immediately
|
|
// start sending data if required.
|
|
//
|
|
|
|
Connection = (PTP_CONNECTION)(SendContext->Owner);
|
|
|
|
IoAcquireCancelSpinLock (&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
|
|
|
p = RemoveHeadList (&Connection->InProgressRequest);
|
|
|
|
//
|
|
// Turn off the connection request timer if there is one, and set
|
|
// this connection's state to READY.
|
|
//
|
|
|
|
Connection->Flags |= CONNECTION_FLAGS_READY;
|
|
|
|
INCREMENT_COUNTER (Connection->Provider, OpenConnections);
|
|
|
|
//
|
|
// Record that the connect request has been successfully
|
|
// completed by TpCompleteRequest.
|
|
//
|
|
|
|
Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED;
|
|
|
|
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
|
|
|
//
|
|
// Now complete the request and get out.
|
|
//
|
|
|
|
if (p == &Connection->InProgressRequest) {
|
|
Connection->IndicationInProgress = FALSE;
|
|
PANIC ("ProcessSessionConfirm: TdiConnect evaporated!\n");
|
|
IoReleaseCancelSpinLock (cancelirql);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We have a completed connection with a queued connect. Complete
|
|
// the connect.
|
|
//
|
|
|
|
request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
|
request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket);
|
|
remoteInformation =
|
|
((PTDI_REQUEST_KERNEL)(&IrpSp->Parameters))->ReturnConnectionInformation;
|
|
if (remoteInformation != NULL) {
|
|
try {
|
|
if (remoteInformation->RemoteAddressLength != 0) {
|
|
|
|
//
|
|
// Build a temporary TA_NETBIOS_ADDRESS, then
|
|
// copy over as many bytes as fit.
|
|
//
|
|
|
|
TdiBuildNetbiosAddress(
|
|
Connection->CalledAddress.NetbiosName,
|
|
(BOOLEAN)(Connection->CalledAddress.NetbiosNameType ==
|
|
TDI_ADDRESS_NETBIOS_TYPE_GROUP),
|
|
&TempAddress);
|
|
|
|
if (remoteInformation->RemoteAddressLength >=
|
|
sizeof (TA_NETBIOS_ADDRESS)) {
|
|
|
|
returnLength = sizeof(TA_NETBIOS_ADDRESS);
|
|
remoteInformation->RemoteAddressLength = returnLength;
|
|
|
|
} else {
|
|
|
|
returnLength = remoteInformation->RemoteAddressLength;
|
|
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
(PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress,
|
|
&TempAddress,
|
|
returnLength);
|
|
|
|
} else {
|
|
|
|
returnLength = 0;
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
returnLength = 0;
|
|
status = GetExceptionCode ();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
returnLength = 0;
|
|
|
|
}
|
|
|
|
RtlCopyMemory( Connection->RemoteName, Connection->CalledAddress.NetbiosName, 16 );
|
|
Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID;
|
|
|
|
//
|
|
// Reference the connection so it stays around after
|
|
// the request is completed.
|
|
//
|
|
|
|
StReferenceConnection("Connect completed", Connection);
|
|
|
|
StCompleteRequest (request, status, returnLength);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
StDestroyPacket(Packet);
|
|
|
|
} /* StSendCompletionHandler */
|
|
|
|
|
|
VOID
|
|
StNdisSend(
|
|
IN PTP_PACKET Packet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends an NDIS packet
|
|
This routine is used to ensure that receive sequence numbers on
|
|
packets are numbered correctly. It is called in place of NdisSend
|
|
and 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 link spinlock held,
|
|
and it returns with it released.
|
|
|
|
Arguments:
|
|
|
|
Packet - Pointer to a TP_PACKET object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NDIS_STATUS NdisStatus;
|
|
|
|
NdisSend (
|
|
&NdisStatus,
|
|
((PDEVICE_CONTEXT)(Packet->Provider))->NdisBindingHandle,
|
|
Packet->NdisPacket);
|
|
|
|
if (NdisStatus != NDIS_STATUS_PENDING) {
|
|
|
|
StSendCompletionHandler(
|
|
Packet->Provider,
|
|
Packet->NdisPacket,
|
|
NdisStatus);
|
|
}
|
|
|
|
} /* StNdisSend */
|
|
|
|
|
|
|
|
NTSTATUS
|
|
BuildBufferChainFromMdlChain (
|
|
IN NDIS_HANDLE BufferPoolHandle,
|
|
IN PMDL CurrentMdl,
|
|
IN ULONG ByteOffset,
|
|
IN ULONG DesiredLength,
|
|
OUT PNDIS_BUFFER *Destination,
|
|
OUT PMDL *NewCurrentMdl,
|
|
OUT ULONG *NewByteOffset,
|
|
OUT ULONG *TrueLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to build an NDIS_BUFFER chain from a source Mdl 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.
|
|
|
|
The NDIS_BUFFERs that are returned are mapped and locked. (Actually, the pages in
|
|
them are in the same state as those in the source MDLs.)
|
|
|
|
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. TrueLength is set to 0.
|
|
|
|
Environment:
|
|
|
|
Kernel Mode, Source Mdls locked. It is recommended, although not required,
|
|
that the source Mdls be mapped and locked prior to calling this routine.
|
|
|
|
Arguments:
|
|
|
|
BufferPoolHandle - The buffer pool to allocate buffers from.
|
|
|
|
CurrentMdl - Points to the start of the Mdl chain from which to draw the
|
|
packet.
|
|
|
|
ByteOffset - Offset within this MDL to start the packet at.
|
|
|
|
DesiredLength - The number of bytes to insert into the packet.
|
|
|
|
Destination - returned pointer to the NDIS_BUFFER chain describing the packet.
|
|
|
|
NewCurrentMdl - returned pointer to the Mdl that would be used for the next
|
|
byte of packet. NULL if the source Mdl chain was exhausted.
|
|
|
|
NewByteOffset - returned offset into the NewCurrentMdl for the next byte of
|
|
packet. NULL if the source Mdl chain was exhausted.
|
|
|
|
TrueLength - The actual length of the returned NDIS_BUFFER Chain. If less than
|
|
DesiredLength, the source Mdl chain was exhausted.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded (even if
|
|
shorter than the desired chain).
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while building the
|
|
destination chain.
|
|
|
|
--*/
|
|
{
|
|
ULONG AvailableBytes;
|
|
PMDL OldMdl;
|
|
PNDIS_BUFFER NewNdisBuffer;
|
|
NDIS_STATUS NdisStatus;
|
|
|
|
|
|
AvailableBytes = MmGetMdlByteCount (CurrentMdl) - ByteOffset;
|
|
if (AvailableBytes > DesiredLength) {
|
|
AvailableBytes = DesiredLength;
|
|
}
|
|
|
|
OldMdl = CurrentMdl;
|
|
*NewCurrentMdl = OldMdl;
|
|
*NewByteOffset = ByteOffset + AvailableBytes;
|
|
*TrueLength = AvailableBytes;
|
|
|
|
|
|
//
|
|
// Build the first NDIS_BUFFER, which could conceivably be the only one...
|
|
//
|
|
|
|
NdisCopyBuffer(
|
|
&NdisStatus,
|
|
&NewNdisBuffer,
|
|
BufferPoolHandle,
|
|
OldMdl,
|
|
ByteOffset,
|
|
AvailableBytes);
|
|
|
|
|
|
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
|
*NewByteOffset = ByteOffset;
|
|
*TrueLength = 0;
|
|
*Destination = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
*Destination = NewNdisBuffer;
|
|
|
|
//
|
|
// Was the first NDIS_BUFFER enough data, or are we out of Mdls?
|
|
//
|
|
|
|
if ((AvailableBytes == DesiredLength) || (OldMdl->Next == NULL)) {
|
|
if (*NewByteOffset >= MmGetMdlByteCount (OldMdl)) {
|
|
*NewCurrentMdl = OldMdl->Next;
|
|
*NewByteOffset = 0;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Need more data, so follow the in Mdl chain to create a packet.
|
|
//
|
|
|
|
OldMdl = OldMdl->Next;
|
|
*NewCurrentMdl = OldMdl;
|
|
|
|
while (OldMdl != NULL) {
|
|
AvailableBytes = DesiredLength - *TrueLength;
|
|
if (AvailableBytes > MmGetMdlByteCount (OldMdl)) {
|
|
AvailableBytes = MmGetMdlByteCount (OldMdl);
|
|
}
|
|
|
|
NdisCopyBuffer(
|
|
&NdisStatus,
|
|
&(NDIS_BUFFER_LINKAGE(NewNdisBuffer)),
|
|
BufferPoolHandle,
|
|
OldMdl,
|
|
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 (*Destination != NULL) {
|
|
NewNdisBuffer = NDIS_BUFFER_LINKAGE(*Destination);
|
|
NdisFreeBuffer (*Destination);
|
|
*Destination = NewNdisBuffer;
|
|
}
|
|
|
|
*NewByteOffset = ByteOffset;
|
|
*TrueLength = 0;
|
|
*NewCurrentMdl = CurrentMdl;
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer);
|
|
|
|
*TrueLength += AvailableBytes;
|
|
*NewByteOffset = AvailableBytes;
|
|
|
|
if (*TrueLength == DesiredLength) {
|
|
if (*NewByteOffset == MmGetMdlByteCount (OldMdl)) {
|
|
*NewCurrentMdl = OldMdl->Next;
|
|
*NewByteOffset = 0;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
OldMdl = OldMdl->Next;
|
|
*NewCurrentMdl = OldMdl;
|
|
|
|
} // while (mdl chain exists)
|
|
|
|
*NewCurrentMdl = NULL;
|
|
*NewByteOffset = 0;
|
|
return STATUS_SUCCESS;
|
|
|
|
} // BuildBufferChainFromMdlChain
|
|
|