/*++

Copyright (c) 1996-1999  Microsoft Corporation

Module Name:

    clstate.c

Abstract:

    state machine for gpc client vcs

Author:

    Yoram Bernet    (yoramb)    28-Dec-1997
    Rajesh Sundaram (rajeshsu)  01-Aug-1998

Environment:

    Kernel Mode

Revision History:

    Rajesh Sundaram (rajeshsu) 04-Apr-1998 - reworked completly as another state 
                                            (CL_INTERNAL_CALL_COMPLETE) added.

--*/

#include "psched.h"
#pragma hdrstop

/* External */

/* Static */

/* Forward */

/* End Forward */

/*++

Routine Description:

    Initiate a close call on this VC and notify the GPC. Always called with the VC lock held.

Return Value:

    None

--*/
VOID
InternalCloseCall(
    PGPC_CLIENT_VC Vc
    )
{
    PADAPTER Adapter = Vc->Adapter;

    PsDbgOut(DBG_INFO,
             DBG_STATE,
             ("[InternalCloseCall]: Adapter %08X, ClVcState is %s on VC %x\n",
              Vc->Adapter, GpcVcState[Vc->ClVcState], Vc));

    switch(Vc->ClVcState){

      case CL_INTERNAL_CLOSE_PENDING:
          
          //
          // We could get here if we get an unbind at our wan instance with a 
          // NDIS_STATUS_WAN_LINE_DOWN. We could be trying to do an InternalClose
          // from both places. 
          //

          PsAssert(Vc->Flags & INTERNAL_CLOSE_REQUESTED);

          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);
          
          break;
          
      case CL_CALL_PENDING:
      case CL_MODIFY_PENDING:

          //
          // We've been asked to close before the call 
          // has completed. 
          //
          // Set a flag so that we'll close it when the 
          // call completes.
          //

          PsAssert(!(Vc->Flags & GPC_CLOSE_REQUESTED));

          Vc->Flags |= INTERNAL_CLOSE_REQUESTED;

          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);

          break;

      case CL_INTERNAL_CALL_COMPLETE:

          //
          // The call has been completed, but we may or may not 
          // have told the GPC. Wait till we tell the GPC. We will
          // complete this when we transistion to CL_CALL_COMPLETE
          //
          
          PsAssert(!IsBestEffortVc(Vc));
          
          Vc->Flags |= INTERNAL_CLOSE_REQUESTED;

          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);
          
          break;
          
      case CL_CALL_COMPLETE:
          
          //
          // Transition to CL_INTERNAL_CLOSE_PENDING and
          // ask the GPC to close.
          //
          
          Vc->ClVcState = CL_INTERNAL_CLOSE_PENDING;

          Vc->Flags |= INTERNAL_CLOSE_REQUESTED;

          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);

          CmCloseCall(Vc);

          break;
        
      case CL_GPC_CLOSE_PENDING:

          //
          // The GPC has already asked us to close. Now, 
          // we are also closing down - We need not do 
          // anything here - Need not even inform the GPC.
          // we can just pretend as the InternalClose never
          // happened

          Vc->Flags &= ~INTERNAL_CLOSE_REQUESTED;

          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);

          break;
        
      default:
          
          PS_UNLOCK_DPC(&Vc->Lock);

          PS_UNLOCK(&Adapter->Lock);
          
          PsDbgOut(DBG_FAILURE,
                   DBG_STATE,
                   ("[InternalCloseCall]: invalid state %s on VC %x\n",
                    GpcVcState[Vc->ClVcState], Vc));

          PsAssert(0);
          break;
    }
}

VOID
CallSucceededStateTransition(
    PGPC_CLIENT_VC Vc
    )
{

    PsDbgOut(DBG_INFO,
             DBG_STATE,
             ("[CallSucceededStateTransition]: Adapter %08X, ClVcState is %s on VC %x\n",
              Vc->Adapter, GpcVcState[Vc->ClVcState], Vc));

    PS_LOCK(&Vc->Lock);

    switch(Vc->ClVcState){

      case CL_GPC_CLOSE_PENDING:

          PS_UNLOCK(&Vc->Lock);
          
          PsDbgOut(DBG_FAILURE,
                   DBG_STATE,
                   ("[CallSucceededStateTransition]: bad state %s on VC %x\n",
                    GpcVcState[Vc->ClVcState], Vc));
          
          PsAssert(0);

          break;

      case CL_INTERNAL_CALL_COMPLETE:
        
          PsAssert(!IsBestEffortVc(Vc));

#if DBG
          if(Vc->Flags & GPC_MODIFY_REQUESTED) {
              PsAssert(! (Vc->Flags & GPC_CLOSE_REQUESTED));
          }

          if(Vc->Flags & GPC_CLOSE_REQUESTED) {
              PsAssert(! (Vc->Flags & GPC_MODIFY_REQUESTED));
          }
#endif
          //
          // Note that if both a modify & a internal remove is requested, 
          // we just satisfy the modify. When the modify goes into internal
          // call complete, we will satify the remove
          //
          if(Vc->Flags & GPC_MODIFY_REQUESTED) {

              NDIS_STATUS Status;

              Vc->ClVcState = CL_MODIFY_PENDING;
              Vc->Flags &= ~GPC_MODIFY_REQUESTED;

              PS_UNLOCK(&Vc->Lock);

              Status = CmModifyCall(Vc);

              if(Status != NDIS_STATUS_PENDING) {
                  
                  CmModifyCallComplete(Status, Vc, Vc->ModifyCallParameters);
              }
              
              break;
              
          }
          
          if(Vc->Flags & GPC_CLOSE_REQUESTED) {
              
              // 
              // The GPC has asked us to close after it
              // was notified of the call completion but
              // before we managed to transition to the
              // CL_CALL_COMPLETE state.
              //
            
              Vc->ClVcState = CL_GPC_CLOSE_PENDING;

              PS_UNLOCK(&Vc->Lock);

              CmCloseCall(Vc);

              break;
          }
          
          if(Vc->Flags & INTERNAL_CLOSE_REQUESTED){
              
              //
              // We had an internal close request while
              // the call was pending. The GPC has already
              // been notified, so - we need to ask it to
              // close.
              //
              
              Vc->ClVcState = CL_INTERNAL_CLOSE_PENDING;

              PS_UNLOCK(&Vc->Lock);

              CmCloseCall(Vc);

              break;
          }
          
          Vc->ClVcState = CL_CALL_COMPLETE;

          PS_UNLOCK(&Vc->Lock);

          break;
          
      case CL_MODIFY_PENDING:
          //
          // Typically, just transition to CL_CALL_COMPLETE
          //
          PsAssert(!(Vc->Flags & GPC_CLOSE_REQUESTED));
          PsAssert(!(Vc->Flags & GPC_MODIFY_REQUESTED));
          PsAssert(!IsBestEffortVc(Vc));
          
          Vc->ClVcState = CL_INTERNAL_CALL_COMPLETE;
          
          PS_UNLOCK(&Vc->Lock);
          
          break;
          
      case CL_CALL_PENDING:
          //
          // Typically, just transition to CL_INTERNAL_CALL_COMPLETE.
          //
          PsAssert(!(Vc->Flags & GPC_CLOSE_REQUESTED));
          PsAssert(!(Vc->Flags & GPC_MODIFY_REQUESTED));
          
          //
          // Call succeeded. Leave it up.
          //
          if(IsBestEffortVc(Vc)) 
          {
              Vc->ClVcState = CL_CALL_COMPLETE;
          }
          else 
          {
            Vc->ClVcState = CL_INTERNAL_CALL_COMPLETE;
          }
          PS_UNLOCK(&Vc->Lock);

          break;
          
      default:
          
          PS_UNLOCK(&Vc->Lock);
          
          PsDbgOut(DBG_FAILURE,
                   DBG_STATE,
                   ("[CallSucceededStateTransition]: invalid state %s on VC %x\n",
                    GpcVcState[Vc->ClVcState], Vc));
          
          PsAssert(0);
    }
}

        
/* end clstate.c */