/*++

*******************************************************************************
* Copyright (c) 1994-95 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This  software  contains  proprietary  and  confidential   information   of *
* Digi International  Inc.   By  accepting  transfer  of this copy, Recipient *
* agrees to retain this software in  confidence,  to  prevent  disclosure  to *
* others,  and  to make no  use of this software other than that for which it *
* was delivered.  This is an unpublished copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1994-95                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************

Module Name:

   msg.c

Abstract:

        This module encapsulates all transactions to and from the DStreams (or DsApi)
        interface.  Protocol specific modules (FR, X25, etc.) use routines in this
        module rather than directly accessing the DsApi.  This keeps common mgmt 
        issues isolated to this one module.

        All manipulations of these structs (alloc'ing, freeing, setting params) is 
        managed by routines in this module to hide implementation details of the 
        actual structures used.  Reason: we     encapsulate the S_xxx structs inside our 
        own DIGILAN_xxx structs so we can 1)    reduce the number of allocations per 
        struct, and 2) keep extra information along with the struct.  See msg.h 
        for details on the DIGILAN_xxx structs.

Author:

   Michael Zarns

Revision History:

   $History: msg.c $
 * 
 * *****************  Version 10  *****************
 * User: Mikez        Date: 6/19/97    Time: 12:05p
 * Updated in $/Components/Windows/NT/Sync/Build/Lan/Src
 * Mods to support new SNID based firmware api for Frame Relay.
 * 
 * *****************  Version 9  *****************
 * User: Mikez        Date: 4/28/97    Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Build/Lan/Src
 * Updated to use DsApi library.
 * 
 * *****************  Version 8  *****************
 * User: Mikez        Date: 10/25/96   Time: 8:39a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * 
 * *****************  Version 7  *****************
 * User: Mikez        Date: 10/24/96   Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added support for new firmware release, 1.1.1B.  Changes to support new
 * synchronous SxbWrapper api SxbOpenWait.
 * 
 * *****************  Version 6  *****************
 * User: Mikez        Date: 10/03/96   Time: 10:15a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Pulled Sxb interface code out of this driver and use newly created Sxb
 * interface wrapper library, SxbWrap.
 * 
 * *****************  Version 5  *****************
 * User: Mikez        Date: 9/26/96    Time: 11:29a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added code to reschedule a GetMsg if one could not be allocated
 * immediately.
 * 
 * *****************  Version 4  *****************
 * User: Mikez        Date: 9/26/96    Time: 8:23a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Moved all Sxb interface routines into two source files - msg.c and
 * irp.c.  
 * 
 * *****************  Version 3  *****************
 * User: Mikez        Date: 9/19/96    Time: 9:23a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added X25 PVC support.
 * 
 * *****************  Version 2  *****************
 * User: Mikez        Date: 8/13/96    Time: 10:48a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Fully abstracted Frame Relay code into three routines:  FrSend,
 * FrReceive and FrInit, and one AdapterObject field: RxControlLength.
 * This revision works and support Frame Relay only.
 * 
 * *****************  Version 1  *****************
 * User: Mikez        Date: 8/09/96    Time: 9:08a
 * Created in $/Components/Windows/NT/Sync/Lan

--*/
#include "digilan.h"
#include "DsApi.h"

///////////////////////////////////////////////////////////////////////////////
//
// Internal Function Prototypes
//

VOID
DlwGetMsgComplete(
        IN HANDLE hOwner,
        IN PVOID Context,
        IN PVOID pControl,
        IN ULONG ControlLength,
        IN PVOID pData,
        IN ULONG DataLength,
        IN NTSTATUS IoStatus );

VOID
DlwPutMsgComplete(
        IN HANDLE hOwner,
        IN PVOID Context,
        IN PUCHAR pControl,
        IN ULONG ControlLength,
        IN PUCHAR pData,
        IN ULONG DataLength,
        IN NTSTATUS IoStatus );

// XXX: Temporary debug assistance
#if DBG
#define MAX_GETRING     100
#define TYPE_GET        'g'
#define TYPE_PUT        'p'

KSPIN_LOCK      gRingLock;

typedef struct DLWRING {
    BOOLEAN             InUse;
    CHAR                Type;
    PVOID               Caller;
    PVOID               pAdapter;
    PVOID               pStream;
    PVOID               pData;
    LARGE_INTEGER       IssueTime;
} DLWRING, *PDLWRING;

DLWRING gGetRing[MAX_GETRING];

__inline __declspec(naked) PVOID GetCaller() {
    __asm { mov eax, dword ptr [ebp+4] }
    __asm { ret }
}

BOOLEAN DlwBuffersAllocated(PVOID pAdapter,PVOID hStream) {
    KIRQL oldIrql;
    PDLWRING pEnt;
    BOOLEAN Ret = FALSE;

    int i;
    KeAcquireSpinLock(&gRingLock,&oldIrql);

    for (i=0,pEnt=gGetRing; i<MAX_GETRING; i++,pEnt++) {
        if (pEnt->InUse &&
            pEnt->pAdapter == pAdapter &&
            pEnt->pStream  == hStream) {
            if (!Ret) {
                SRAPRINT(DBGLVL_ALWAYS,
                         ("Found Msg Leak, Adapter:%x Stream:%x\n",
                          pAdapter,hStream));
            }
            Ret = TRUE;
            SRAPRINT(DBGLVL_ALWAYS,
                     ("Type:%c Data:%x\n",pEnt->Type,pEnt->pData));
        }
    }

    KeReleaseSpinLock(&gRingLock,oldIrql);

    return Ret;
}

VOID
dlwAddRing(PVOID Caller, CHAR Type,PVOID pAdapter,PVOID pStream, PVOID pData) {
    KIRQL oldIrql;
    PDLWRING pEnt;

    int i;
    KeAcquireSpinLock(&gRingLock,&oldIrql);

    for (i=0,pEnt=gGetRing; i<MAX_GETRING; i++,pEnt++) {
        if (!pEnt->InUse) {
            pEnt->InUse = TRUE;
            pEnt->Type = Type;
            pEnt->Caller = Caller;
            pEnt->pAdapter = pAdapter;
            pEnt->pStream = pStream;
            pEnt->pData = pData;
            KeQuerySystemTime(&pEnt->IssueTime);
            break;
        }
    }

    KeReleaseSpinLock(&gRingLock,oldIrql);
}

VOID
dlwRemRing(PVOID pAdapter,PVOID pStream, PVOID pData) {
    KIRQL oldIrql;
    PDLWRING pEnt;

    int i;
    KeAcquireSpinLock(&gRingLock,&oldIrql);

    for (i=0,pEnt=gGetRing; i<MAX_GETRING; i++,pEnt++) {
        if (pEnt->InUse &&
            pEnt->pAdapter == pAdapter &&
            pEnt->pStream == pStream &&
            pEnt->pData == pData) {
            pEnt->InUse = FALSE;
            break;
        }
    }

    KeReleaseSpinLock(&gRingLock,oldIrql);
}

#define ADDGETRING(pAdapter,pStream,pData) dlwAddRing(GetCaller(),TYPE_GET,pAdapter,pStream,pData)
#define ADDPUTRING(pAdapter,pStream,pData) dlwAddRing(GetCaller(),TYPE_PUT,pAdapter,pStream,pData)
#define REMRING(pAdapter,pStream,pData) dlwRemRing(pAdapter,pStream,pData)

#else

BOOLEAN DlwBuffersAllocated(PVOID pAdapter,PVOID hStream) { return FALSE; }

#define ADDGETRING(pAdapter,pStream,pData) 
#define ADDPUTRING(pAdapter,pStream,pData) 
#define REMRING(pAdapter,pStream,pData) 

#endif


NDIS_STATUS
DlwOpen(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PCSZ pcszDeviceName )
/*++

Description:

        This routine encapsulates the DStreams Open Stream (IOCTL_DS_OPEN IRP) 
        request.  The returned hStream value in the adapter object's hStream field.

Parameters:

        pAdapter
                The adapter object which defines both the ndis virtual adapter and X.25
                PVC (these two are synonymous in this context).

        pcszDeviceName
                String containing the name of the device to open, of the form "X250" or "FR".

Return Value:

        NDIS_STATUS_SUCCESS, or some error.

--*/
{
    NDIS_STATUS Status;
    ULONG Error = 0;

    DigiDump( DIGIFLOW, ("Enter DlwOpen\n") );
    if ( !pAdapter )
        return( STATUS_INVALID_PARAMETER_1 );
    if ( !pcszDeviceName )
        return( STATUS_INVALID_PARAMETER_2 );

    // Synchronous Open Stream request
    Status = DsOpen( pAdapter->hDevice, pcszDeviceName, pAdapter, NULL, 0,
                     &pAdapter->hStream, &Error );

    if ( NT_ERROR( Status ) || Error )
        return( NDIS_STATUS_FAILURE );

    DigiDump( DIGIFLOW, ("Leave DlwOpen\n") );
    return( Status );
}



NDIS_STATUS
DlwIoctl(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN ULONG Ioctl,
        IN PUCHAR pData,
        IN ULONG DataLength,
        OUT PULONG pReturnCode )
/*++

Description:

        This routine encapsulates a DStreams Ioctl request used to send ioctls down to 
        the firmware.

Parameters:

        pAdapter
                The adapter object which defines both the ndis virtual adapter and X.25
                PVC (these two are synonymous in this context) on which this ioctl is to
                sent.

        Ioctl
                The ioctl command to send.

        pData
                The data field of the ioctl.

        DataLength
                Length in bytes of the data field.

        pReturnCode
                ReturnCode returned by sxb firmware.  

Return Value:

        NDIS_STATUS_SUCCESS, or appropriate error.

--*/
{
    NTSTATUS Status;

    ENTER( "DlwIoctl" );

    Status = DsIoctl( pAdapter->hStream, Ioctl, pData, DataLength, pReturnCode );

    DigiDump( DIGIFLOW, ("Leave DlwIoctl, status 0x%x\n", Status) );
    return( Status );
}



NTSTATUS
DlwPutMsg(
        IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
        IN PUCHAR pControlBuffer,
        IN ULONG ControlLength,
        IN PNDIS_PACKET pNdisPacket,
        IN ULONG DataOffset,
        IN ULONG DataLength
        )
/*++

Description:

        This routine along with its counterpart, PutMsgComplete encapsulate the 
        PutMsg and Irp usage for sending packets.  Allocs, initializes and sends
        the requested data in a PutMsg wrapped in an Irp.

Parameters:

        pAdapter
                The adapter object which describes the VC on which this data should be sent.

        pControlBuffer
                Address of control header.  The entire control buffer is copied into the 
                PutMsg so caller may free it immediately after return from this routine.

        ControlLength
                Length of control header.  This defines how many bytes at pControlBuffer
                are copied into the PutMsg.

        pNdisPacket
                Pointer to original NdisPacket.  Inserted into the PutMsg to be retrieved at
                CompletionRoutine time when we send up the NdisSendComplete acknowledgement.
        
        DataOffset
                Offset into the ndis packet at which to start copying data.  This defines
                the payload of the packet which should be sent over the wire.  A new 
                contiguous buffer will be alloc'd to hold the packet.

        DataLength
                Length of data packet.  This value is inserted into the PutMsg.  This
                defines the size of data buffer alloc'd.

Return Value:

        The return value from IoCallDriver, or some error.

--*/
{
    NDIS_STATUS Status;
    PUCHAR pData = NULL;

    ENTER( "DlwPutMsg" );

    ASSERT( pAdapter && pControlBuffer && ControlLength );

    if ( pNdisPacket )
    {
        ASSERT( DataLength );
        pData = DigiAllocMem( NonPagedPool, DataLength );

        if ( !pData )
        {
            DigiDump( DIGIERRORS, ("***DlwPutMsg: Could not alloc data buffer!\n") );
            LEAVE( "DlwPutMsg", NDIS_STATUS_RESOURCES );
        }

        Status = DigiGetNdisPacketData( pData, pNdisPacket, DataLength, DataOffset );

        if ( !NT_SUCCESS( Status ) )
        {
            DigiDump( DIGIERRORS, ("***DlwPutMsg: Error in send NdisPacket!\n") );
            DigiFreeMem( pData );
            LEAVE( "DlwPutMsg", Status );
        }
    }

    ADDPUTRING(pAdapter,pAdapter->hStream,pData);
    SRAPRINT2(DBGTYPE_USR1,DBGLVL_SUMMARY,
              ("DlwPutMsg: Ad:%x Str:%x pData:%x pNDISPacket:%x\n",
               pAdapter,pAdapter->hStream,pData,pNdisPacket));

    Status = DsPutMsgEx( pAdapter->hStream, pControlBuffer, ControlLength,
                         pData, DataLength, 
                         pNdisPacket, DS_PRIORITY_LOW, DlwPutMsgComplete );

    if ( !NT_SUCCESS( Status ) )
    {
        // DsPutMsgEx only returns PENDING or ERROR, and when it returns ERROR, 
        // that means the Irp couldn't be sent and the completion routine won't
        // be called.  Free the buffer now.
        DigiDump( DIGIERRORS, ("***DlwPutMsg: DsPutMsgEx failed (0x%x)\n", Status ) );
        if (pData)
            DigiFreeMem( pData );
    }

    LEAVE( "DlwPutMsg", Status );
}



VOID
DlwPutMsgComplete(
        IN HANDLE hOwner,
        IN PVOID Context,
        IN PUCHAR pControl,
        IN ULONG ControlLength,
        IN PUCHAR pData,
        IN ULONG DataLength,
        IN NTSTATUS IoStatus )
/*++

Description:

        This is the completion routine for DStreams PutMsg ioctls sent down to the
        DStreams driver.  It handles freeing the memory allocated for the Irp, and 
        queuing up an NDIS SendComplete indication on the adapter context queue.

Parameters:

        hOwner
                Contains the address of the DIGILAN_LOGICAL_ADAPTER on which this request
                originated.

        Context
                Pointer to original NdisPacket.

        pControl
                Pointer to control portion of PutMsg.

        ControlLength
                Length of control portion.

        pData
                Pointer to data portion of PutMsg.

        DataLength
                Length of data block.  Not used.

        IoStatus
                IoStatus.Status returned from Sxb.

Return Value:

        None

--*/
{
    PDIGILAN_LOGICAL_ADAPTER pAdapter;

    DigiDump( DIGIFLOW, ("Enter DlwPutMsgComplete\n") );
        
    pAdapter = (PDIGILAN_LOGICAL_ADAPTER) hOwner;
    ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

    REMRING(pAdapter,pAdapter->hStream,pData);
    SRAPRINT2(DBGTYPE_USR1,DBGLVL_SUMMARY,
              ("DlwPutMsgComplete: Ad:%x Str:%x pData:%x IoStatus:%x\n",
               pAdapter,pAdapter->hStream,pData,IoStatus));

    pAdapter->SyncProtocol.SendComplete( pAdapter,
                                         pControl,
                                         ControlLength,
                                         (PVOID)Context,
                                         IoStatus );

    // Free the buffers we alloc'd in Send routine
    if ( pData )
        DigiFreeMem( pData );
    DigiDump( DIGIFLOW, ("Leave DlwPutMsgComplete\n") );
}



NDIS_STATUS
DlwGetMsg(
        PDIGILAN_LOGICAL_ADAPTER pAdapter )
/*++

Description:

        This routine alloc's, initializes and sends a new GetMsg request down to 
        the DStreams provider driver.

Parameters:

        pAdapter
                Address of the adapter object.

Return Value:

        NDIS_STATUS_SUCCESS to indicate the request was sent, or
        NDIS_STATUS_RESOURCES if the request could not be allocated.

--*/
{
    PDIGILAN_FRAME_BUFFER pBuffer;
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

    DigiDump( DIGIFLOW, ("Enter DlwGetMsg\n") );

#ifdef STRATUS_DEVICE
    // windxb-114: first thing to do is check whether DigiLanHalt is currently running.
    // Exit right away if so, otherwise we get memory leakage.

    NdisAcquireSpinLock( &pAdapter->Lock );
    if (pAdapter->HaltReceived)
    {
        NdisReleaseSpinLock( &pAdapter->Lock );
        return NDIS_STATUS_FAILURE;
    }
    
    NdisReleaseSpinLock( &pAdapter->Lock );
#endif

    //
    // Since DigiSxb can't notify us of received data, we must continually send
    // down GetMsg requests for data.  Keep sending down GetMsg requests until
    // Sxb returns STATUS_PENDING, indicating there's no more receive data 
    // waiting.  GetMsgCompletion will later re-start this GetMsg pump.
    //

    pBuffer = NULL;

    //
    // Alloc data frame
    //
        
    pBuffer = DigiAllocMem( NonPagedPool, sizeof( DIGILAN_FRAME_BUFFER ) );
    if ( !pBuffer )
    {
        DigiDump( (DIGIERRORS|DIGIINIT), 
                  ("*** DlwGetMsg: Could not alloc data buffer!\n") );
        Status = NDIS_STATUS_RESOURCES;
    }           
    else
    {
        // Send the request
        ADDGETRING(pAdapter,pAdapter->hStream,pBuffer);
        SRAPRINT2(DBGTYPE_USR1,DBGLVL_SUMMARY,
                  ("DlwGetMsg: Ad:%x Str:%x pBuffer:%x\n",
                   pAdapter,pAdapter->hStream,pBuffer));

        Status = DsGetMsgEx( pAdapter->hStream,
                             pAdapter->SyncProtocol.RxControlLength,
                             pBuffer->Data, ETHERNET_MAX_FRAME_SIZE,
                             pBuffer, DS_PRIORITY_LOW, DlwGetMsgComplete );
    }

    if ( NT_ERROR( Status ) )
    {
        // DsGetMsgEx only returns PENDING or ERROR, and when it returns ERROR, 
        // that means the Irp couldn't be sent and the completion routine won't
        // be called.  Free the buffer now.  A PENDING return is not an ERROR.
        DigiDump( DIGIERRORS, ("*** DlwGetMsg: GetMsg request failed (0x%x)\n",Status ) );
                
        if ( pBuffer )
            DigiFreeMem( pBuffer );

    }

    DigiDump( DIGIFLOW, ("Leave DlwGetMsg, status 0x%x\n", Status) );
    return( Status );

}



VOID
DlwGetMsgComplete(
        IN HANDLE hOwner,
        IN PVOID Context,
        IN PVOID pControl,
        IN ULONG ControlLength,
        IN PVOID pData,
        IN ULONG DataLength,
        IN NTSTATUS IoStatus )
/*++

Description:

        This routine is called when the DStreams provider completes a GetMsg 
        request, which is sent down by DlwGetMsg.

Parameters:

        hOwner
                Contains the address of the DIGILAN_LOGICAL_ADAPTER on which this request
                originated.

        Context
                Pointer to beginning of originally allocated data buffer.

        pControl
                Pointer to control portion of GetMsg.

        ControlLength
                Length of control portion.

        pData
                Pointer to start of data inside data buffer of GetMsg.

        DataLength
                Length of data, starting at pData.

        IOStatus
                IoStatus.Status returned from Sxb.

Return Value:

        None

--*/
{
    PDIGILAN_LOGICAL_ADAPTER pAdapter;
    DigiDump( DIGIFLOW, ("Enter DlwGetMsgComplete\n") );

    pAdapter = (PDIGILAN_LOGICAL_ADAPTER)hOwner;
    ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

    REMRING(pAdapter,pAdapter->hStream,Context);
    SRAPRINT2(DBGTYPE_USR1,DBGLVL_SUMMARY,
              ("DlwGetMsgComplete: Ad:%x Str:%x pBuffer:%x IoStat:%x\n",
               pAdapter,pAdapter->hStream,Context,IoStatus));

    if ( IoStatus == STATUS_SUCCESS )
    {

        DigiDump( DIGIRX, 
                  ( "DigiLan GetMsg completed:\n"
                    "   pAdapter =  0x%x\n"
                    "   Line = 0x%x\n"
                    "   Length: %d (0x%x)\n",
                    pAdapter,
                    pAdapter->Snid,
                    DataLength,
                    DataLength ) );

        // Process received frame
        pAdapter->SyncProtocol.Receive( pAdapter,
                                        pControl,
                                        Context,
                                        DataLength );
#ifdef STRATUS_DEVICE
        // Trigger new get message only if we're not halting (windxb-114):

        NdisAcquireSpinLock( &pAdapter->Lock );
        if (!pAdapter->HaltReceived)
#endif
        {
            LARGE_INTEGER Timeout;
            Timeout.QuadPart = -10000;  // 1 ms timeout
            KeSetTimer( &pAdapter->GetMsgTimer, Timeout, &pAdapter->GetMsgDpc );
        }
#ifdef STRATUS_DEVICE
        NdisReleaseSpinLock( &pAdapter->Lock );
#endif
    }
    else
    {
        //BUGBUG ? Even in error case we need to trigger GetMsg to prevent that we run dry

        DigiDump( DIGIERRORS, ("*** DlwGetMsgComplete: IoStatus 0x%x\n", IoStatus ) );
        DigiFreeMem( Context );
    }
       
    DigiDump( DIGIFLOW, ("Leave DlwGetMsgComplete\n") );
}



VOID 
DlwGetMsgTimeout( 
        IN PKDPC Dpc,
        IN PVOID DeferredContext,
        IN PVOID SystemContext1,
        IN PVOID SystemContext2 
        )
/*++

Description:

        This routine is called when a previous getmsg was unsuccessful and needed 
        to be rescheduled.

Parameters:

        DeferredContext
                This is a pointer to the Adapter object for this PVC.

--*/
{
    PDIGILAN_LOGICAL_ADAPTER pAdapter = DeferredContext;
    ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

    DlwGetMsg( pAdapter );
}


