/*******************************************************************/
/*	      Copyright(c)  1995 Microsoft Corporation		   */
/*******************************************************************/

//***
//
// Filename:	rmhand.c
//
// Description: This module contains the procedures for the
//		        DDM's procedure-driven state machine
//              that handles RasMan events.
//
//              NOTE:Rasman should be modified to set a flag when a frame is
//                   received or and state change has occurred. This will save
//                   DDM from getting info for all the ports.
//
// Author:	    Stefan Solomon (stefans)    May 26, 1992.
//
//***
#include "ddm.h"
#include "timer.h"
#include "handlers.h"
#include "objects.h"
#include "util.h"
#include "routerif.h"
#include <raserror.h>
#include <rasppp.h>
#include <ddmif.h>
#include <serial.h>
#include "rasmanif.h"
#include <ras.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>

//***
//
//  Function:       SvDevConnected
//
//  Description:	Handles the device transition to connected state
//
//***
VOID
SvDevConnected(
    IN PDEVICE_OBJECT pDeviceObj
)
{
    PCONNECTION_OBJECT  pConnObj;
    HCONN               hConnection;
    DWORD               dwRetCode;
    LPWSTR              auditstrp[3];

    DDM_PRINT( gblDDMConfigInfo.dwTraceId, TRACE_FSM,
	           "SvDevConnected: Entered, hPort=%d", pDeviceObj->hPort);

    //
    // Get handle to the connection or bundle for this link
    //

    if ( RasPortGetBundle( NULL,
                           pDeviceObj->hPort,
                           &hConnection ) != NO_ERROR )
    {
	    DevStartClosing(pDeviceObj);

        return;
    }


    switch (pDeviceObj->DeviceState)
    {
	case DEV_OBJ_LISTEN_COMPLETE:

        pDeviceObj->hConnection = hConnection;

        //
	    // reset the H/W Error signal state
        //

	    pDeviceObj->dwHwErrorSignalCount = HW_FAILURE_CNT;

        //
	    // get the system time for this connection
        //

	    GetLocalTime( &pDeviceObj->ConnectionTime );

        //
	    // get the frame broadcasted by the client
        //

	    if ( ( dwRetCode = RmReceiveFrame( pDeviceObj ) ) != NO_ERROR )
        {
            //
		    // can't get the broadcast frame. This is a fatal error
		    // Log the error
            //

		    auditstrp[0] = pDeviceObj->wchPortName;

		    DDMLogErrorString( ROUTERLOG_CANT_RECEIVE_FRAME, 1, auditstrp,
                               dwRetCode, 1);

		    DevStartClosing( pDeviceObj );
	    }
	    else
	    {
            //
		    // switch to frame receiving state
            //

		    pDeviceObj->DeviceState = DEV_OBJ_RECEIVING_FRAME;

            if ( RAS_DEVICE_TYPE( pDeviceObj->dwDeviceType ) != RDT_Atm )
            {
                //
		        // start authentication timer
                //

		        TimerQRemove( (HANDLE)pDeviceObj->hPort, SvAuthTimeout );

		        TimerQInsert( (HANDLE)pDeviceObj->hPort,
                              gblDDMConfigInfo.dwAuthenticateTime,
                              SvAuthTimeout );
            }
	    }

	    break;

	case DEV_OBJ_CALLBACK_CONNECTING:

        {

        //
        // log on the client disconnection
        //

        WCHAR   wchFullUserName[UNLEN+DNLEN+2];

        if ( pDeviceObj->wchDomainName[0] != TEXT('\0') )
        {
            wcscpy( wchFullUserName, pDeviceObj->wchDomainName );
            wcscat( wchFullUserName, TEXT("\\") );
            wcscat( wchFullUserName, pDeviceObj->wchUserName );
        }
        else
        {
            wcscpy( wchFullUserName, pDeviceObj->wchUserName );
        }

        auditstrp[0] = wchFullUserName;
        auditstrp[1] = pDeviceObj->wchPortName;
        auditstrp[2] = pDeviceObj->wchCallbackNumber;

        DDMLogInformation( ROUTERLOG_CLIENT_CALLED_BACK, 3, auditstrp);

        }

        //
	    // set up the new state
        //

	    pDeviceObj->DeviceState = DEV_OBJ_AUTH_IS_ACTIVE;

        //
	    // start authentication timer
        //

	    TimerQRemove( (HANDLE)pDeviceObj->hPort, SvAuthTimeout );

	    TimerQInsert( (HANDLE)pDeviceObj->hPort,
                      gblDDMConfigInfo.dwAuthenticateTime,
                      SvAuthTimeout );

        //
	    // and tell auth to restart conversation
        //

        if ( pDeviceObj->fFlags & DEV_OBJ_IS_PPP )
        {
            //
            // Need to set framing to PPP to make callback over ISDN
            // work.
            //

            RAS_FRAMING_INFO RasFramingInfo;

            ZeroMemory( &RasFramingInfo, sizeof( RasFramingInfo ) );

            //
            // Default ACCM for PPP is 0xFFFFFFFF
            //

            RasFramingInfo.RFI_RecvACCM         = 0xFFFFFFFF;
            RasFramingInfo.RFI_SendACCM         = 0xFFFFFFFF;
            RasFramingInfo.RFI_MaxSendFrameSize = 1500;
            RasFramingInfo.RFI_MaxRecvFrameSize = 1500;
            RasFramingInfo.RFI_SendFramingBits  = PPP_FRAMING;
            RasFramingInfo.RFI_RecvFramingBits  = PPP_FRAMING;

            RasPortSetFramingEx( pDeviceObj->hPort, &RasFramingInfo );

            pDeviceObj->hConnection = hConnection;

            PppDdmCallbackDone(pDeviceObj->hPort, pDeviceObj->wchCallbackNumber);
        }
        else
        {
            // We only suport PPP framing in the server
            //
            
            RTASSERT(FALSE);
        }

	    break;

	default:

	    break;
    }
}

//***
//
//  Function:	SvDevDisconnected
//
//  Descr:	Handles the device transition to disconnected state
//
//***

VOID
SvDevDisconnected(
    IN PDEVICE_OBJECT pDeviceObj
)
{
    DDM_PRINT( gblDDMConfigInfo.dwTraceId, TRACE_FSM,
	           "SvDevDisconnected:Entered, hPort=%d",pDeviceObj->hPort);

    switch (pDeviceObj->DeviceState)
    {
	case DEV_OBJ_LISTENING:

        //
	    // h/w error; start h/w error timer
        //

	    pDeviceObj->DeviceState = DEV_OBJ_HW_FAILURE;

	    TimerQRemove( (HANDLE)pDeviceObj->hPort, SvHwErrDelayCompleted );

	    TimerQInsert( (HANDLE)pDeviceObj->hPort, HW_FAILURE_WAIT_TIME,
                      SvHwErrDelayCompleted );

        //
	    // if hw error has not been signaled for this port,
	    // decrement the counter and signal when 0
        //

	    if(pDeviceObj->dwHwErrorSignalCount)
        {
		    pDeviceObj->dwHwErrorSignalCount--;

		    if(pDeviceObj->dwHwErrorSignalCount == 0)
            {
		        SignalHwError(pDeviceObj);
		    }
	    }

	    break;

	case DEV_OBJ_CALLBACK_DISCONNECTING:

        //
	    // disconnection done; can start waiting the callback delay
        //

	    pDeviceObj->DeviceState = DEV_OBJ_CALLBACK_DISCONNECTED;

	    TimerQRemove( (HANDLE)pDeviceObj->hPort, SvCbDelayCompleted );

	    TimerQInsert( (HANDLE)pDeviceObj->hPort, pDeviceObj->dwCallbackDelay,
                          SvCbDelayCompleted);

	    break;

	case DEV_OBJ_CALLBACK_CONNECTING:

        if (gblDDMConfigInfo.dwCallbackRetries > pDeviceObj->dwCallbackRetries)
        {
            DDMTRACE( "Callback failed, retrying" );

            pDeviceObj->dwCallbackRetries++;

            pDeviceObj->DeviceState = DEV_OBJ_CALLBACK_DISCONNECTED;

            TimerQRemove( (HANDLE)pDeviceObj->hPort, SvCbDelayCompleted );

            TimerQInsert( (HANDLE)pDeviceObj->hPort,
                          pDeviceObj->dwCallbackDelay,
                          SvCbDelayCompleted );
            break;
        }

    case DEV_OBJ_LISTEN_COMPLETE:
	case DEV_OBJ_RECEIVING_FRAME:
	case DEV_OBJ_AUTH_IS_ACTIVE:

        //
	    // accidental disconnection; clean-up and restart on this device
        //

	    DevStartClosing( pDeviceObj );

	    break;

	case DEV_OBJ_ACTIVE:

	    DevStartClosing(pDeviceObj);

	    break;

	case DEV_OBJ_CLOSING:

	    DevCloseComplete(pDeviceObj);
	    break;

	default:

	    break;
    }
}

VOID
SvDevListenComplete(
    IN PDEVICE_OBJECT pDeviceObj
)
{
    LPWSTR  auditstrp[1];
    DWORD   dwLength;
    DWORD   dwRetCode;
    DWORD   dwBucketIndex = DeviceObjHashPortToBucket( pDeviceObj->hPort );

    //
    // We reset these values here is case they were set for dialout and the
    // dialout failed, we may have not been able to clean up in
    // the RasConnectCallback routine in rasapiif.c since the
    // RasGetSubEntryHandle may have failed and we hence do not get a
    // pointer to the port so we could not cleanup.
    //

    pDeviceObj->DeviceState             = DEV_OBJ_LISTEN_COMPLETE;
    pDeviceObj->fFlags                  &= ~DEV_OBJ_OPENED_FOR_DIALOUT;
    pDeviceObj->fFlags                  &= ~DEV_OBJ_SECURITY_DLL_USED;
    pDeviceObj->hConnection             = (HCONN)INVALID_HANDLE_VALUE;
    pDeviceObj->wchUserName[0]          = (WCHAR)NULL;
    pDeviceObj->wchDomainName[0]        = (WCHAR)NULL;
    pDeviceObj->wchCallbackNumber[0]    = (WCHAR)NULL;
    pDeviceObj->hRasConn                = (HRASCONN)NULL;
    pDeviceObj->pRasmanSendBuffer       = NULL;
    pDeviceObj->pRasmanRecvBuffer       = NULL;
    pDeviceObj->dwCallbackRetries       = 0;

	pDeviceObj->dwRecvBufferLen = 1500;

    dwRetCode = RasGetBuffer((CHAR**)&pDeviceObj->pRasmanRecvBuffer,
                             &((pDeviceObj->dwRecvBufferLen)) );

    if ( dwRetCode != NO_ERROR )
    {
        auditstrp[0] = pDeviceObj->wchPortName;

	    DDMLogErrorString( ROUTERLOG_CANT_RECEIVE_BYTES, 1, auditstrp,
                           dwRetCode, 1);

        DevStartClosing(pDeviceObj);

        return;
    }

    //
    // If the security DLL is not loaded or we are not serial, simply
    // change the state
    //

    if ( ( gblDDMConfigInfo.lpfnRasBeginSecurityDialog == NULL ) ||
         ( gblDDMConfigInfo.lpfnRasEndSecurityDialog   == NULL ) )
    {
        //
        // Change RASMAN state to CONNECTED from LISTENCOMPLETE and signal
        // RmEventHandler
        //

        if ( RasPortConnectComplete(pDeviceObj->hPort) != NO_ERROR )
        {
            DevStartClosing(pDeviceObj);
            return;
        }

        SetEvent( gblSupervisorEvents[NUM_DDM_EVENTS+dwBucketIndex] );
    }
    else
    {
        // Otherwise call the security dll ordinal to begin the 3rd party
        // security dialog with the client

        dwLength = 1500;

        dwRetCode = RasGetBuffer((CHAR**)&pDeviceObj->pRasmanSendBuffer,
                                 &dwLength );

        if ( dwRetCode != NO_ERROR )
        {
            auditstrp[0] = pDeviceObj->wchPortName;

	        DDMLogErrorString( ROUTERLOG_CANT_RECEIVE_BYTES, 1, auditstrp,
                           dwRetCode, 1);

            DevStartClosing(pDeviceObj);

            return;
        }


        //
        // Make sure that this device type supports raw mode.
        //

        if ( RasPortSend( pDeviceObj->hPort,
                          (CHAR*)pDeviceObj->pRasmanSendBuffer,
                          0 ) != NO_ERROR )
        {
            RasFreeBuffer( pDeviceObj->pRasmanSendBuffer );

            pDeviceObj->pRasmanSendBuffer = NULL;

            //
            // Change RASMAN state to CONNECTED from LISTENCOMPLETE and signal
            // RmEventHandler
            //

            if ( RasPortConnectComplete( pDeviceObj->hPort ) != NO_ERROR )
            {
                DevStartClosing(pDeviceObj);
                return;
            }

            SetEvent( gblSupervisorEvents[NUM_DDM_EVENTS+dwBucketIndex] );

            return;
        }

        dwRetCode = (*gblDDMConfigInfo.lpfnRasBeginSecurityDialog)(
                                                pDeviceObj->hPort,
                                                pDeviceObj->pRasmanSendBuffer ,
                                                dwLength,
                                                pDeviceObj->pRasmanRecvBuffer,
		                                        pDeviceObj->dwRecvBufferLen,
                                                RasSecurityDialogComplete );

        if ( dwRetCode != NO_ERROR )
        {
            //
            // Audit failure due to error and hangup the line
            //

            auditstrp[0] = pDeviceObj->wchPortName;

	        DDMLogErrorString( ROUTERLOG_SEC_AUTH_INTERNAL_ERROR,1,auditstrp,
                               dwRetCode, 1);

            DevStartClosing(pDeviceObj);

            return;
        }
        else
        {
            pDeviceObj->SecurityState = DEV_OBJ_SECURITY_DIALOG_ACTIVE;

            pDeviceObj->fFlags |= DEV_OBJ_SECURITY_DLL_USED;

            //
            // Start timer for 3rd party security
            //

	        TimerQRemove( (HANDLE)pDeviceObj->hPort, SvSecurityTimeout );

	        TimerQInsert( (HANDLE)pDeviceObj->hPort,
                          gblDDMConfigInfo.dwSecurityTime,
                          SvSecurityTimeout);
        }
    }

    return;
}

//
//*** Array of previous connection state/ current connection state
//    used to select the Ras Manager signaled event handler
//

typedef VOID  (* RMEVHDLR)(PDEVICE_OBJECT);

typedef struct _RMEHNODE
{
    RASMAN_STATE previous_state;
    RASMAN_STATE current_state;
    RMEVHDLR rmevhandler;

} RMEHNODE, *PRMEHNODE;


RMEHNODE rmehtab[] =
{
    //	 Transition
    // Previous --> Current

    { CONNECTING,       CONNECTED,	            SvDevConnected },
    { LISTENING,        LISTENCOMPLETED,        SvDevListenComplete },
    { LISTENCOMPLETED,  CONNECTED,              SvDevConnected },
    { LISTENCOMPLETED,  DISCONNECTED,           SvDevDisconnected },
    { LISTENING,        DISCONNECTED,           SvDevDisconnected },
    { CONNECTED,        DISCONNECTED,           SvDevDisconnected },
    { DISCONNECTING,	DISCONNECTED,		    SvDevDisconnected },
    { CONNECTED,	    CONNECTING,		        SvDevDisconnected },
    { 0xffff,           0xffff,                 NULL }// Table Guard
};

VOID
RmEventHandler(
    DWORD dwEventIndex
)
{
    RASMAN_INFO     RasPortInfo;
    PDEVICE_OBJECT  pDevObj;
    PRMEHNODE       ehnp;
    DWORD           dwRetCode;
    DWORD           dwBucketIndex = dwEventIndex - NUM_DDM_EVENTS;

    EnterCriticalSection( &(gblDeviceTable.CriticalSection) );

    //
    // for each port in this bucket
    //

    for ( pDevObj = gblDeviceTable.DeviceBucket[dwBucketIndex];
          pDevObj != (DEVICE_OBJECT *)NULL;
          pDevObj = pDevObj->pNext )
    {
        //
	    // get the port state
        //

        dwRetCode = RasGetInfo( NULL, pDevObj->hPort, &RasPortInfo );

        if ( dwRetCode != NO_ERROR )
        {
            SetLastError( dwRetCode );

            DDMTRACE3( "RasGetInfo( 0x%x, 0x%x ) = %d",
                       pDevObj->hPort, &RasPortInfo, dwRetCode );

            //
            // Assume the the port is disconnected
            //

            pDevObj->ConnectionState = DISCONNECTED;

            SvDevDisconnected( pDevObj );

            continue;
        }

        //
	    // check if we own the port now
        //

	    if (!RasPortInfo.RI_OwnershipFlag)
        {
            //
	        // skip biplexed ports used by other processes
            //

	        continue;
	    }

        //
	    // switch on our private connection state
        //

	    switch (pDevObj->ConnectionState)
        {
	    case CONNECTING:

            if (RasPortInfo.RI_ConnState == CONNECTING)
            {
	            switch (RasPortInfo.RI_LastError)
                {
	            case SUCCESS:

                    RasPortConnectComplete(pDevObj->hPort);

                    //
		            // force current state to connected.
                    //

	                RasPortInfo.RI_ConnState = CONNECTED;

		            break;

                case PENDING:

                    //
                    // no action
                    //

	                break;

                default:

                    //
	                // error occured -> force state to disconnecting
                    //

		            pDevObj->ConnectionState = DISCONNECTING;

                    DDM_PRINT(
                        gblDDMConfigInfo.dwTraceId,
                        TRACE_FSM,
                        "RmEventHandler: RI_LastError indicates error when");
                    DDM_PRINT(
                        gblDDMConfigInfo.dwTraceId,
                        TRACE_FSM,
                        " CONNECTING on port %d !!!\n", pDevObj->hPort );
                    DDM_PRINT(
                        gblDDMConfigInfo.dwTraceId,
                        TRACE_FSM,
	                    "RmEventHandler:RasPortDisconnect posted on port%d\n",
                        pDevObj->hPort);

	                if ( pDevObj->DeviceState == DEV_OBJ_CALLBACK_CONNECTING )
                    {
                        LPWSTR Parms[3];
                        WCHAR  wchFullUserName[UNLEN+DNLEN+2];

                        if ( pDevObj->wchDomainName[0] != TEXT('\0') )
                        {
                            wcscpy( wchFullUserName, pDevObj->wchDomainName);
                            wcscat( wchFullUserName, TEXT("\\") );
                            wcscat( wchFullUserName, pDevObj->wchUserName );
                        }
                        else
                        {
                            wcscpy( wchFullUserName, pDevObj->wchUserName );
                        }

                        Parms[0] = wchFullUserName;
                        Parms[1] = pDevObj->wchPortName;
                        Parms[2] = pDevObj->wchCallbackNumber;

                        DDMLogErrorString(ROUTERLOG_CALLBACK_FAILURE, 3, Parms,
                                          RasPortInfo.RI_LastError, 3 );
                    }

	                dwRetCode = RasPortDisconnect(
                                        pDevObj->hPort,
                                        gblSupervisorEvents[NUM_DDM_EVENTS +
                                                            dwBucketIndex ] );

	                RTASSERT((dwRetCode == PENDING) || (dwRetCode == SUCCESS));

		            break;
                }
            }

            break;

	    case LISTENING:

	        if (RasPortInfo.RI_ConnState != LISTENING)
            {
                break;
            }

	        switch (RasPortInfo.RI_LastError)
            {
	        case PENDING:

                //
                // no action
                //

	            break;

            default:

                //
                // error occured -> force state to disconnecting
                //

                pDevObj->ConnectionState = DISCONNECTING;

                DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
                   "RmEventHandler: RI_LastError indicates error %d when",
                    RasPortInfo.RI_LastError );
                DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
                   " LISTENING on port %d !!!\n", pDevObj->hPort );
                DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
                   "RmEventHandler:RasPortDisconnect posted on port%d\n",
                   pDevObj->hPort);

                dwRetCode = RasPortDisconnect(
                                        pDevObj->hPort,
                                        gblSupervisorEvents[NUM_DDM_EVENTS +
                                                            dwBucketIndex ] );

                RTASSERT((dwRetCode == PENDING) || (dwRetCode == SUCCESS));

                break;
            }

            break;

	    default:

            break;

	    }

        //
	    // try to find the table element with the matching previous and
	    // current connection states
        //

	    for (ehnp=rmehtab; ehnp->rmevhandler != NULL; ehnp++)
        {
	        if ((ehnp->previous_state == pDevObj->ConnectionState) &&
	            (ehnp->current_state == RasPortInfo.RI_ConnState))
            {
		        //
		        //*** Match ***
		        //

                DDM_PRINT(
                   gblDDMConfigInfo.dwTraceId,
                   TRACE_FSM,
	               "Rasman state change received from port %d, %d->%d",
                   pDevObj->hPort, ehnp->previous_state, ehnp->current_state );

                //
		        // change the dcb conn state (previous state) with the
		        // current state
                //

		        pDevObj->ConnectionState = RasPortInfo.RI_ConnState;

                //
		        // invoke the handler
                //

		        (*ehnp->rmevhandler)(pDevObj);

		        break;
	        }
	    }
    }

    LeaveCriticalSection( &(gblDeviceTable.CriticalSection) );
}

//***
//
//  Function:	SvFrameReceived
//
//  Descr:	starts authentication
//
//***
VOID
SvFrameReceived(
    IN PDEVICE_OBJECT   pDeviceObj,
    IN CHAR             *framep,  // pointer to the received frame
    IN DWORD            framelen,
    IN DWORD            dwBucketIndex
)
{
    DWORD               dwRetCode;
    DWORD               FrameType;
    LPWSTR              portnamep;
    PCONNECTION_OBJECT  pConnObj;
    BYTE                RecvBuffer[1500];

    DDM_PRINT( gblDDMConfigInfo.dwTraceId, TRACE_FSM,
	           "SvFrameReceived: Entered, hPort: %d", pDeviceObj->hPort);

    if ( framelen > sizeof( RecvBuffer ) )
    {
        DDMTRACE2( "Illegal frame length of %d received for port %d", 
                    framelen, pDeviceObj->hPort );

        RTASSERT( FALSE );

        //
        // Frame length is illegal so truncate it
        //

        framelen = sizeof( RecvBuffer );
    }

    memcpy( RecvBuffer, framep, framelen);

    switch (pDeviceObj->DeviceState)
    {
	case DEV_OBJ_RECEIVING_FRAME:

	    if ( !DDMRecognizeFrame( RecvBuffer, (WORD)framelen, &FrameType) )
        {
            portnamep = pDeviceObj->wchPortName;

            DDMLogError(ROUTERLOG_UNRECOGNIZABLE_FRAME_RECVD, 1, &portnamep, 0);

            DevStartClosing(pDeviceObj);

            return;
        }

        //
	    // check first with our authentication module
        //

	    switch( FrameType )
        {

        case PPP_LCP_PROTOCOL:

            pDeviceObj->fFlags |= DEV_OBJ_IS_PPP;

            DDM_PRINT( gblDDMConfigInfo.dwTraceId, TRACE_FSM,
                       "SvFrameReceived: PPP frame on port %d",
                       pDeviceObj->hPort);

            dwRetCode = PppDdmStart( pDeviceObj->hPort,
                                     pDeviceObj->wchPortName,
                                     RecvBuffer,
                                     framelen,
                                     gblDDMConfigInfo.dwAuthenticateRetries
                                   );

            if ( dwRetCode != NO_ERROR )
            {
                portnamep = pDeviceObj->wchPortName;

                DDMLogErrorString( ROUTERLOG_CANT_START_PPP, 1, &portnamep,
                                   dwRetCode,1);

                DevStartClosing(pDeviceObj);

                return;
            }

            break;

        case APPLETALK:

            DDM_PRINT( gblDDMConfigInfo.dwTraceId, TRACE_FSM,
                       "SvFrameReceived: protocol not supported! %d",
                       pDeviceObj->hPort);

            RTASSERT( FALSE );


            break;

        default:

            break;
        }

        //
        // auth has started OK. Update state
        // start auth timer
        //

        pDeviceObj->DeviceState = DEV_OBJ_AUTH_IS_ACTIVE;
        pDeviceObj->fFlags |= DEV_OBJ_AUTH_ACTIVE;

	    break;

	case DEV_OBJ_CLOSING:

	    DevCloseComplete(pDeviceObj);

	    break;

	default:

	    break;
    }
}

//***
//
//  Function:	    RmRecvFrameEventHandler
//
//  Description:	Scans the set of opened ports and detects the ports where
//		            RasPortReceive has completed. Invokes the FSM handling
//		            procedure for each detected port and frees the receive
//		            buffer.
//
//***
VOID
RmRecvFrameEventHandler(
    DWORD dwEventIndex
)
{
    PDEVICE_OBJECT      pDevObj;
    RASMAN_INFO         RasPortInfo;
    DWORD               dwRetCode;
    DWORD               dwBucketIndex = dwEventIndex
                                        - NUM_DDM_EVENTS
                                        - gblDeviceTable.NumDeviceBuckets;

    EnterCriticalSection( &(gblDeviceTable.CriticalSection) );

    //
    // for each port in this bucket
    //

    for ( pDevObj = gblDeviceTable.DeviceBucket[dwBucketIndex];
          pDevObj != (DEVICE_OBJECT *)NULL;
          pDevObj = pDevObj->pNext )
    {
        //
	    // get the port state
        //

        dwRetCode = RasGetInfo( NULL, pDevObj->hPort, &RasPortInfo );

        if ( dwRetCode != NO_ERROR )
        {
            //
            // Assume port is disconncted, so clean up
            //

            DevStartClosing(pDevObj);

            continue;
        }

        //
	    // check if we own the port now
        //

	    if (!RasPortInfo.RI_OwnershipFlag)
        {
            //
	        // skip biplexed ports used by other processes
            //

	        continue;
	    }

        if ( ( pDevObj->fFlags & DEV_OBJ_RECEIVE_ACTIVE ) &&
             ( RasPortInfo.RI_LastError != PENDING ) )
        {
            //
            // recv frame API has completed
            //

            pDevObj->fFlags &= (~DEV_OBJ_RECEIVE_ACTIVE );

            if ( RasPortInfo.RI_LastError != ERROR_PORT_DISCONNECTED )
            {
               LPBYTE lpBuffer = LocalAlloc(LPTR,RasPortInfo.RI_BytesReceived);

               if ( lpBuffer == NULL )
               {
                   DevStartClosing(pDevObj);

                   continue;
               }

               memcpy( lpBuffer,
                       pDevObj->pRasmanRecvBuffer,
                       RasPortInfo.RI_BytesReceived );

               RasFreeBuffer(pDevObj->pRasmanRecvBuffer);

               pDevObj->pRasmanRecvBuffer = NULL;

               //
               // call the FSM handler
               //

               SvFrameReceived( pDevObj,
                                lpBuffer,
                                RasPortInfo.RI_BytesReceived,
                                dwBucketIndex);

               LocalFree( lpBuffer );
            }

            if ( pDevObj->pRasmanRecvBuffer != NULL )
            {
                RasFreeBuffer(pDevObj->pRasmanRecvBuffer);

                pDevObj->pRasmanRecvBuffer = NULL;
            }
        }
    }

    LeaveCriticalSection( &(gblDeviceTable.CriticalSection) );
}