File:  [WindowsNT SDKs] / ntddk / src / network / tdi / ind.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 18:31:12 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: ntddk-nov-1993, HEAD
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993

/*++

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)ExInterlockedIncrementLong(
            (PLONG)&DeviceContext->ReceivePacketExhausted,
            &DeviceContext->Interlock);
        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)ExInterlockedIncrementLong(
            (PLONG)&DeviceContext->ReceiveBufferExhausted,
            &DeviceContext->Interlock);
        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 */


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.