2020-09-30 17:12:29 +02:00

830 lines
22 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989-1993 Microsoft Corporation
Module Name:
ind.c
Abstract:
This module contains code which implements the indication handler
for the NT Sample transport provider.
Environment:
Kernel mode
Revision History:
--*/
#include "st.h"
NDIS_STATUS
StReceiveIndication (
IN NDIS_HANDLE BindingContext,
IN NDIS_HANDLE ReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
)
/*++
Routine Description:
This routine receives control from the physical provider as an
indication that a frame has been received on the physical link.
This routine is time critical, so we only allocate a
buffer and copy the packet into it. We also perform minimal
validation on this packet. It gets queued to the device context
to allow for processing later.
Arguments:
BindingContext - The Adapter Binding specified at initialization time.
ReceiveContext - A magic cookie for the MAC.
HeaderBuffer - pointer to a buffer containing the packet header.
HeaderBufferSize - the size of the header.
LookaheadBuffer - pointer to a buffer containing the negotiated minimum
amount of buffer I get to look at (not including header).
LookaheadBufferSize - the size of the above. May be less than asked
for, if that's all there is.
PacketSize - Overall size of the packet (not including header).
Return Value:
NDIS_STATUS - status of operation, one of:
NDIS_STATUS_SUCCESS if packet accepted,
NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol,
NDIS_any_other_thing if I understand, but can't handle.
--*/
{
PDEVICE_CONTEXT DeviceContext;
HARDWARE_ADDRESS SourceAddressBuffer;
PHARDWARE_ADDRESS SourceAddress;
UINT RealPacketSize;
PST_HEADER StHeader;
DeviceContext = (PDEVICE_CONTEXT)BindingContext;
RealPacketSize = 0;
//
// Obtain the packet length; this may optionally adjust
// the lookahead buffer forward if the header we wish
// to remove spills over into what the MAC considers
// data. If it determines that the header is not
// valid, it keeps RealPacketSize at 0.
//
MacReturnPacketLength(
&DeviceContext->MacInfo,
HeaderBuffer,
HeaderBufferSize,
PacketSize,
&RealPacketSize
);
if (RealPacketSize < 2) {
return NDIS_STATUS_NOT_RECOGNIZED;
}
//
// We've negotiated at least a contiguous DLC header passed back in the
// lookahead buffer. Check it to see if we want this packet.
//
StHeader = (PST_HEADER)LookaheadBuffer;
if (StHeader->Signature != ST_SIGNATURE) {
return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed.
}
//
// Check that the packet is not too long.
//
if (PacketSize > DeviceContext->MaxReceivePacketSize) {
#if DBG
StPrint2("StReceiveIndication: Ignoring packet length %d, max %d\n",
PacketSize, DeviceContext->MaxReceivePacketSize);
#endif
return NDIS_STATUS_NOT_RECOGNIZED;
}
MacReturnSourceAddress(
&DeviceContext->MacInfo,
HeaderBuffer,
&SourceAddressBuffer,
&SourceAddress
);
return StGeneralReceiveHandler(
DeviceContext,
ReceiveContext,
SourceAddress,
HeaderBuffer, // header
RealPacketSize, // total data length in packet
(PST_HEADER)LookaheadBuffer, // lookahead data
LookaheadBufferSize // lookahead data length
);
}
NDIS_STATUS
StGeneralReceiveHandler (
IN PDEVICE_CONTEXT DeviceContext,
IN NDIS_HANDLE ReceiveContext,
IN PHARDWARE_ADDRESS SourceAddress,
IN PVOID HeaderBuffer,
IN UINT PacketSize,
IN PST_HEADER StHeader,
IN UINT StSize
)
/*++
Routine Description:
This routine receives control from StReceiveIndication.
It continues the processing of indicated data.
This routine is time critical, so we only allocate a
buffer and copy the packet into it. We also perform minimal
validation on this packet. It gets queued to the device context
to allow for processing later.
Arguments:
DeviceContext - The device context of this adapter.
ReceiveContext - A magic cookie for the MAC.
SourceAddress - The source address of the packet.
HeaderBuffer - pointer to the packet header.
PacketSize - Overall size of the packet (not including header).
DlcHeader - Points to the DLC header of the packet.
DlcSize - The length of the packet indicated, starting from DlcHeader.
Return Value:
NDIS_STATUS - status of operation, one of:
NDIS_STATUS_SUCCESS if packet accepted,
NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol,
NDIS_any_other_thing if I understand, but can't handle.
--*/
{
KIRQL oldirql;
NTSTATUS Status;
NDIS_STATUS NdisStatus;
PNDIS_PACKET NdisPacket;
PNDIS_BUFFER NdisBuffer;
PSINGLE_LIST_ENTRY linkage;
UINT BytesTransferred;
PRECEIVE_PACKET_TAG ReceiveTag;
PBUFFER_TAG BufferTag;
PUCHAR SourceRouting;
UINT SourceRoutingLength;
PTP_ADDRESS DatagramAddress;
UINT NdisBufferLength;
PTP_CONNECTION Connection;
PVOID BufferPointer;
INCREMENT_COUNTER (DeviceContext, PacketsReceived);
Status = STATUS_SUCCESS; // assume no further processing required
//
// See what type of frame this is.
//
if ((StHeader->Command == ST_CMD_CONNECT) ||
(StHeader->Command == ST_CMD_DATAGRAM)) {
MacReturnSourceRouting(
&DeviceContext->MacInfo,
HeaderBuffer,
&SourceRouting,
&SourceRoutingLength);
Status = StProcessConnectionless (
DeviceContext,
SourceAddress,
StHeader,
StSize,
SourceRouting,
SourceRoutingLength,
&DatagramAddress);
} else if ((StHeader->Command == ST_CMD_INFORMATION) ||
(StHeader->Command == ST_CMD_DISCONNECT)) {
//
// If successful this adds a connection reference.
//
if (!(Connection = StFindConnection(DeviceContext, StHeader->Destination, StHeader->Source))) {
return NDIS_STATUS_NOT_RECOGNIZED;
}
if (StHeader->Command == ST_CMD_INFORMATION) {
Status = StProcessIIndicate (
Connection,
StHeader,
StSize,
PacketSize,
ReceiveContext,
(BOOLEAN)((StHeader->Flags & ST_FLAGS_LAST) != 0)
);
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
StDereferenceConnection ("Information done", Connection);
} else {
Status = STATUS_SUCCESS;
}
} else {
StStopConnection (Connection, STATUS_REMOTE_DISCONNECT);
StDereferenceConnection ("Disconnect done", Connection);
Status = STATUS_SUCCESS;
}
} else {
//
// An unrecognized frame.
//
Status = STATUS_SUCCESS;
}
//
// If the above routines return success, the packet has been processed
// and can be discarded. If they return anything else, the packet needs
// to be copied to local storage for handling in a more lesurely
// fashion.
//
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
return NDIS_STATUS_SUCCESS;
}
linkage = ExInterlockedPopEntryList(
&DeviceContext->ReceivePacketPool,
&DeviceContext->Interlock);
if (linkage != NULL) {
NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] );
} else {
(VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceivePacketExhausted);
return NDIS_STATUS_RESOURCES;
}
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
linkage = ExInterlockedPopEntryList(
&DeviceContext->ReceiveBufferPool,
&DeviceContext->Interlock);
if (linkage != NULL) {
BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage);
} else {
ExInterlockedPushEntryList(
&DeviceContext->ReceivePacketPool,
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
&DeviceContext->Interlock);
(VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceiveBufferExhausted);
return NDIS_STATUS_RESOURCES;
}
NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize);
NdisChainBufferAtFront (NdisPacket, (PNDIS_BUFFER)BufferTag->NdisBuffer);
//
// DatagramAddress has a reference added already.
//
BufferTag->Address = DatagramAddress;
//
// set up async return status so we can tell when it has happened;
// can never get return of NDIS_STATUS_PENDING in synch completion routine
// for NdisTransferData, so we know it has completed when this status
// changes
//
ReceiveTag->NdisStatus = NDIS_STATUS_PENDING;
ReceiveTag->PacketType = TYPE_AT_COMPLETE;
ExInterlockedInsertTailList(
&DeviceContext->ReceiveInProgress,
&ReceiveTag->Linkage,
&DeviceContext->SpinLock);
//
// receive packet is mapped at initalize
//
NdisTransferData (
&NdisStatus,
DeviceContext->NdisBindingHandle,
ReceiveContext,
0,
PacketSize,
NdisPacket,
&BytesTransferred);
//
// handle the various error codes
//
switch (NdisStatus) {
case NDIS_STATUS_SUCCESS: // received packet
ReceiveTag->NdisStatus = NDIS_STATUS_SUCCESS;
if (BytesTransferred == PacketSize) { // Did we get the entire packet?
return NDIS_STATUS_SUCCESS;
}
break;
case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData
return NDIS_STATUS_SUCCESS;
break;
default: // something broke; certainly we'll never get NdisTransferData
// asynch completion with this error status...
break;
}
//
// receive failed, for some reason; cleanup and fail return
//
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
RemoveEntryList (&ReceiveTag->Linkage);
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
ReceiveTag->PacketType = TYPE_AT_INDICATE;
ExInterlockedPushEntryList(
&DeviceContext->ReceivePacketPool,
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
&DeviceContext->Interlock);
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength);
BufferTag = CONTAINING_RECORD (
BufferPointer,
BUFFER_TAG,
Buffer[0]
);
NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value
ExInterlockedPushEntryList(
&DeviceContext->ReceiveBufferPool,
&BufferTag->Linkage,
&DeviceContext->Interlock);
if (DatagramAddress) {
StDereferenceAddress ("DG TransferData failed", DatagramAddress);
}
return NDIS_STATUS_FAILURE;
} // StReceiveIndication
VOID
StTransferDataComplete (
IN NDIS_HANDLE BindingContext,
IN PNDIS_PACKET NdisPacket,
IN NDIS_STATUS NdisStatus,
IN UINT BytesTransferred
)
/*++
Routine Description:
This routine receives control from the physical provider as an
indication that an NdisTransferData has completed. We use this indication
to start stripping buffers from the receive queue.
Arguments:
BindingContext - The Adapter Binding specified at initialization time.
NdisPacket/RequestHandle - An identifier for the request that completed.
NdisStatus - The completion status for the request.
BytesTransferred - Number of bytes actually transferred.
Return Value:
None.
--*/
{
PDEVICE_CONTEXT DeviceContext;
PRECEIVE_PACKET_TAG ReceiveTag;
PTP_CONNECTION Connection;
PNDIS_BUFFER NdisBuffer;
KIRQL oldirql, cancelirql;
//
// Put the NDIS status into a place we can use in packet processing.
// Note that this complete indication may be occuring during the call
// to NdisTransferData in the receive indication.
//
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
//
// note that the processing below depends on having only one packet
// transfer outstanding at a time. NDIS is supposed to guarentee this.
//
switch (ReceiveTag->PacketType) {
case TYPE_AT_COMPLETE: // normal handling
ReceiveTag->NdisStatus = NdisStatus;
break;
case TYPE_AT_INDICATE:
DeviceContext = (PDEVICE_CONTEXT)BindingContext;
Connection = ReceiveTag->Connection;
//
// The transfer for this packet is complete. Was it successful??
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
ULONG DumpData[1];
DumpData[0] = BytesTransferred;
StWriteGeneralErrorLog(
DeviceContext,
EVENT_TRANSPORT_TRANSFER_DATA,
603,
NdisStatus,
NULL,
1,
DumpData);
//
// Drop the packet. BUGBUG: The driver should recover
// from this, but this transport has no way to cause
// the remote to resend.
//
}
//
// Now dereference the request to say we've got no more local
// references to the memory owned by it.
//
Connection->CurrentReceiveRequest->IoRequestPacket->IoStatus.Information += BytesTransferred;
StDereferenceRequest ("TransferData complete", Connection->CurrentReceiveRequest);
//
// see if we've completed the current receive. If so, move to the next one.
//
if (ReceiveTag->CompleteReceive) {
if (ReceiveTag->EndOfMessage) {
//
// The messages has been completely received, ack it.
//
// We set DEFERRED_ACK and DEFERRED_NOT_Q here, which
// will cause an ack to be piggybacked if any data is
// sent during the call to CompleteReceive. If this
// does not happen, then we will call AcknowledgeDataOnlyLast
// which will will send a DATA ACK or queue a request for
// a piggyback ack. We do this *after* calling CompleteReceive
// so we know that we will complete the receive back to
// the client before we ack the data, to prevent the
// next receive from being sent before this one is
// completed.
//
IoAcquireCancelSpinLock(&cancelirql);
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
Connection->Flags2 |= CONNECTION_FLAGS2_RC_PENDING;
} else {
//
// If there is a receive posted, make it current and
// send a receive outstanding.
//
ActivateReceive (Connection);
IoAcquireCancelSpinLock(&cancelirql);
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
}
//
// NOTE: This releases the cancel and connection locks.
//
CompleteReceive (Connection, ReceiveTag->EndOfMessage, oldirql, cancelirql);
}
//
// dereference the connection to say we've done the I frame processing.
// This reference was done before calling NdisTransferData.
//
if (ReceiveTag->TransferDataPended) {
StDereferenceConnection("TransferData done", Connection);
}
//
// rip all of the NDIS_BUFFERs we've used off the chain and return them.
//
if (ReceiveTag->AllocatedNdisBuffer) {
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
while (NdisBuffer != NULL) {
NdisFreeBuffer (NdisBuffer);
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
}
} else {
NdisReinitializePacket (NdisPacket);
}
ExInterlockedPushEntryList(
&DeviceContext->ReceivePacketPool,
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
&DeviceContext->Interlock);
break;
default:
break;
}
return;
} /* StTransferDataComplete */
VOID
StReceiveComplete (
IN NDIS_HANDLE BindingContext
)
/*++
Routine Description:
This routine receives control from the physical provider as an
indication that a connection(less) frame has been received on the
physical link. We dispatch to the correct packet handler here.
Arguments:
BindingContext - The Adapter Binding specified at initialization time.
ST uses the DeviceContext for this parameter.
Return Value:
None
--*/
{
PDEVICE_CONTEXT DeviceContext;
NTSTATUS Status;
KIRQL oldirql, oldirql1;
PLIST_ENTRY linkage;
PNDIS_PACKET NdisPacket;
PNDIS_BUFFER NdisBuffer;
UINT NdisBufferLength;
PVOID BufferPointer;
PRECEIVE_PACKET_TAG ReceiveTag;
PBUFFER_TAG BufferTag;
PTP_ADDRESS Address;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
PTP_CONNECTION Connection;
DeviceContext = (PDEVICE_CONTEXT) BindingContext;
//
// Complete all pending receives. Do a quick check
// without the lock.
//
while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) {
linkage = ExInterlockedRemoveHeadList(
&DeviceContext->IrpCompletionQueue,
&DeviceContext->SpinLock);
if (linkage != NULL) {
Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry);
IrpSp = IoGetCurrentIrpStackLocation (Irp);
Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext;
IoCompleteRequest (Irp, IO_NETWORK_INCREMENT);
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) {
Connection->Flags2 &= ~CONNECTION_FLAGS2_RC_PENDING;
}
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
StDereferenceConnection ("receive completed", Connection);
} else {
//
// ExInterlockedRemoveHeadList returned NULL, so don't
// bother looping back.
//
break;
}
}
//
// Packetize all waiting connections
//
if (!IsListEmpty(&DeviceContext->PacketizeQueue)) {
PacketizeConnections (DeviceContext);
}
//
// Get every waiting packet, in order...
//
if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) {
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) {
linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress);
NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0]);
//
// NdisTransferData may have failed at async completion; check and
// see. If it did, then we discard this packet. If we're still waiting
// for transfer to complete, go back to sleep and hope (no guarantee!)
// we get waken up later.
//
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
if (ReceiveTag->NdisStatus == NDIS_STATUS_PENDING) {
InsertHeadList (&DeviceContext->ReceiveInProgress, linkage);
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
return;
}
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
if (ReceiveTag->NdisStatus != NDIS_STATUS_SUCCESS) {
goto FreePacket; // skip the packet, continue with while loop
}
NdisQueryPacket (NdisPacket, NULL, NULL, &NdisBuffer, NULL);
//
// Have a packet. Since I allocated the storage for it, I know it's
// virtually contiguous and can treat it that way, which I will
// henceforth.
//
NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength);
//
// Determine what address this is for, which is stored
// in the buffer tag header.
//
BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]);
Address = BufferTag->Address;
//
// Process the frame as a UI frame; only datagrams should
// be processed here. If Address is NULL then this datagram
// is not needed for any bound address and should be given
// to RAS only.
//
ASSERT (Address != NULL);
//
// Indicate it or complete posted datagrams.
//
Status = StIndicateDatagram (
DeviceContext,
Address,
BufferPointer,
NdisBufferLength);
//
// Dereference the address.
//
StDereferenceAddress ("Datagram done", Address);
//
// Finished with packet; return to pool.
//
FreePacket:;
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
ReceiveTag->PacketType = TYPE_AT_INDICATE;
ExInterlockedPushEntryList(
&DeviceContext->ReceivePacketPool,
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
&DeviceContext->Interlock);
NdisAdjustBufferLength (NdisBuffer, BufferTag->Length);
ExInterlockedPushEntryList(
&DeviceContext->ReceiveBufferPool,
&BufferTag->Linkage,
&DeviceContext->Interlock);
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
}
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
} // if queue not empty
return;
} /* StReceiveComplete */