/***********************************************************************
 *                                                                     *
 * Filename: fsmapi.c                                                  *
 * Module:   H245 Finite State Machine Subsystem                       *
 *                                                                     *
 ***********************************************************************
 *  INTEL Corporation Proprietary Information                          *
 *                                                                     *
 *  This listing is supplied under the terms of a license agreement    *
 *  with INTEL Corporation and may not be copied nor disclosed except  *
 *  in accordance with the terms of that agreement.                    *
 *                                                                     *
 *      Copyright (c) 1996 Intel Corporation. All rights reserved.     *
 ***********************************************************************
 *                                                                     *
 * $Workfile:   FSMAPI.C  $
 * $Revision:   1.12  $
 * $Modtime:   09 Dec 1996 13:34:24  $
 * $Log L:\mphone\h245\h245env\comm\h245_3\h245_fsm\vcs\src\fsmapi.c_v $
 *                                                                     *
 ***********************************************************************/

#include "precomp.h"

#include "h245api.h"
#include "h245com.h"
#include "h245fsm.h"
#include "h245deb.x"



extern char *EntityName[];



/*
 * This table maps FSM stateless events into H.245 API events
 */
static WORD StatelessTable[NUM_EVENTS - NUM_STATE_EVENTS] =
{
  H245_IND_NONSTANDARD_REQUEST,     // NonStandardRequestPDU
  H245_IND_NONSTANDARD_RESPONSE,    // NonStandardResponsePDU
  H245_IND_NONSTANDARD_COMMAND,     // NonStandardCommandPDU
  H245_IND_NONSTANDARD,             // NonStandardIndicationPDU
  H245_IND_MISC_COMMAND,            // MiscellaneousCommandPDU
  H245_IND_MISC,                    // MiscellaneousIndicationPDU
  H245_IND_COMM_MODE_REQUEST,       // CommunicationModeRequestPDU
  H245_IND_COMM_MODE_RESPONSE,      // CommunicationModeResponsePDU
  H245_IND_COMM_MODE_COMMAND,       // CommunicationModeCommandPDU
  H245_IND_CONFERENCE_REQUEST,      // ConferenceRequestPDU
  H245_IND_CONFERENCE_RESPONSE,     // ConferenceResponsePDU
  H245_IND_CONFERENCE_COMMAND,      // ConferenceCommandPDU
  H245_IND_CONFERENCE,              // ConferenceIndicationPDU
  H245_IND_SEND_TERMCAP,            // SendTerminalCapabilitySetPDU
  H245_IND_ENCRYPTION,              // EncryptionCommandPDU
  H245_IND_FLOW_CONTROL,            // FlowControlCommandPDU
  H245_IND_ENDSESSION,              // EndSessionCommandPDU
  H245_IND_FUNCTION_NOT_UNDERSTOOD, // FunctionNotUnderstoodIndicationPDU
  H245_IND_JITTER,                  // JitterIndicationPDU
  H245_IND_H223_SKEW,               // H223SkewIndicationPDU
  H245_IND_NEW_ATM_VC,              // NewATMVCIndicationPDU
  H245_IND_USERINPUT,               // UserInputIndicationPDU
  H245_IND_H2250_MAX_SKEW,          // H2250MaximumSkewIndicationPDU
  H245_IND_MC_LOCATION,             // MCLocationIndicationPDU
  H245_IND_VENDOR_ID,               // VendorIdentificationIndicationPDU
  H245_IND_FUNCTION_NOT_SUPPORTED,  // FunctionNotSupportedIndicationPDU
};



/*
 * Configurable counter values
 */

unsigned int    uN100 = 10;              // Master Slave Determination



/*
 * Configurable timer values
 */

unsigned int    uT101 = 30000;          // Capability Exchange
unsigned int    uT102 = 30000;          // Maintenance Loop
unsigned int    uT103 = 30000;          // Logical Channel Signalling
unsigned int    uT104 = 30000;          // H.223 Multiplex Table
unsigned int    uT105 = 30000;          // Round Trip Delay
unsigned int    uT106 = 30000;          // Master Slave Determination
unsigned int    uT107 = 30000;          // Request Multiplex Entry
unsigned int    uT108 = 30000;          // Send Logical Channel
unsigned int    uT109 = 30000;          // Mode Request



/*
 *  NAME
 *      ObjectCreate - create an State Entity object
 *
 *
 *  PARAMETERS
 *      INPUT   pInst       Pointer to FSM instance data
 *      INPUT   Entity      State Entity represented by object, e.g. LCSE_OUT
 *      INPUT   Key         Lookup key for distinguish multiple instances of SE
 *      INPUT   dwTransId   Transaction identifier to be sent up to client
 *
 *  RETURN VALUE
 *      pObject     Function succeeded
 *      NULL        Memory allocation failed
 */

Object_t *
ObjectCreate(struct InstanceStruct *pInstance, Entity_t Entity, Key_t Key, DWORD_PTR dwTransId)
{
    register Object_t * pObject;

#if defined(_DEBUG)
    H245TRACE(pInstance->dwInst, 4, "ObjectCreate: Entity=%s(%d) Key=%d dwTransID=0x%p",
              EntityName[Entity], Entity, Key, dwTransId);
#else
    H245TRACE(pInstance->dwInst, 4, "ObjectCreate: Entity=%d Key=%d dwTransID=0x%p",
              Entity, Key, dwTransId);
#endif

    pObject = (Object_t *)MemAlloc(sizeof(*pObject));
    if (pObject == NULL)
    {
        H245TRACE(pInstance->dwInst, 1, "ObjectCreate: FSM Object memory allocation failed");
        return NULL;
    }
    memset(pObject, 0, sizeof(*pObject));

    /* copy primitive variables to my object */
    pObject->pInstance   = pInstance;
    pObject->dwInst      = pInstance->dwInst;
    pObject->dwTransId   = dwTransId;
    pObject->Key         = Key;
    pObject->Entity      = Entity;

    pObject->pNext       = pInstance->StateMachine.Object_tbl[Entity];
    pInstance->StateMachine.Object_tbl[Entity] = pObject;

    return pObject;
} // ObjectCreate()



/*
 *  NAME
 *      ObjectDestroy - deallocate an object created by ObjectCreate()
 *
 *
 *  PARAMETERS
 *  INPUT   pInst       pointer to FSM instance data
 *  INPUT   id          index into the object table
 *
 *  RETURN VALUE
 *      FALSE           object deallocated
 *      TRUE            object not found
 */

int
ObjectDestroy(Object_t *pObject)
{
    struct InstanceStruct * pInstance;
    Object_t *              pSearch;
    Object_t *              pPrev;

    ASSERT(pObject != NULL);
    ASSERT(pObject->uNestLevel == 0);
    ASSERT(pObject->pInstance != NULL);
    pInstance = pObject->pInstance;

#if defined(_DEBUG)
    H245TRACE(pInstance->dwInst, 4, "ObjectDestroy: Entity=%s(%d) Key=%d State=%d",
              EntityName[pObject->Entity], pObject->Entity, pObject->Key, pObject->State);
#else
    H245TRACE(pInstance->dwInst, 4, "ObjectDestroy: Entity=%d Key=%d State=%d",
              pObject->Entity, pObject->Key, pObject->State);
#endif

    if (pObject->dwTimerId)
    {
        H245TRACE(pObject->dwInst, 4, "ObjectDestroy: stoping timer");
        FsmStopTimer(pObject);
    }

    if (pInstance->StateMachine.Object_tbl[pObject->Entity] == NULL)
    {
        H245TRACE(pInstance->dwInst, 1, "ObjectDestroy: no State Entity of specified type found");
        return TRUE;
    }

    if (pInstance->StateMachine.Object_tbl[pObject->Entity] == pObject)
    {
        pInstance->StateMachine.Object_tbl[pObject->Entity] = pObject->pNext;
        MemFree(pObject);
        return FALSE;
    }

    pPrev = pInstance->StateMachine.Object_tbl[pObject->Entity];
    pSearch = pPrev->pNext;
    while (pSearch != NULL)
    {
        if (pSearch == pObject)
        {
            pPrev->pNext = pSearch->pNext;
            MemFree(pObject);
            return FALSE;
        }
        pPrev = pSearch;
        pSearch = pSearch->pNext;
    }

    H245TRACE(pInstance->dwInst, 1, "ObjectDestroy: State Entity not found");
    return TRUE;
} // ObjectDestroy()



/*
 *  NAME
 *      ObjectFind - given parsed information of a PDU, it searches the object table for
 *                         an object with a matching id, type and category
 *
 *
 *  PARAMETERS
 *  INPUT    pInst
 *  INPUT    Category       category of a given PDU
 *  INPUT    Type           type of the PDU
 *  INPUT    pdu_id         unique id shared by PDU and object (usually channel number or sequence number)
 *
 *  RETURN VALUE
 *      pObject   object found
 *      NULL      object not found
 */

Object_t *
ObjectFind(struct InstanceStruct *pInstance, Entity_t Entity, Key_t Key)
{
    register Object_t * pObject;

    ASSERT(Entity < STATELESS);
    pObject = pInstance->StateMachine.Object_tbl[Entity];
    while (pObject != NULL)
    {
        if (pObject->Key == Key)
        {
#if defined(_DEBUG)
            H245TRACE(pInstance->dwInst, 4, "ObjectFind(%s, %d) object found",
                      EntityName[Entity], Key);
#else
            H245TRACE(pInstance->dwInst, 4, "ObjectFind(%d, %d) object found",
                      Entity, Key);
#endif
            return pObject;
        }
        pObject = pObject->pNext;
    }

#if defined(_DEBUG)
    H245TRACE(pInstance->dwInst, 4, "ObjectFind(%s, %d) object not found",
              EntityName[Entity], Key);
#else
    H245TRACE(pInstance->dwInst, 4, "ObjectFind(%d, %d) object not found",
              Entity, Key);
#endif
    return NULL;
} // ObjectFind()



/*
 *  NAME
 *      SendFunctionNotUnderstood - builds and sends Function Not Supported PDU
 *
 *
 *  PARAMETERS
 *      INPUT   dwInst   Current H.245 instance
 *      INPUT   pPdu     Not supported PDU
 *
 *  RETURN VALUE
 *      H245_ERROR_OK
 */


HRESULT
SendFunctionNotUnderstood(struct InstanceStruct *pInstance, PDU_t *pPdu)
{
    PDU_t *             pOut;
    HRESULT             lError;

    pOut = MemAlloc(sizeof(*pOut));
    if (pOut == NULL)
    {
        return H245_ERROR_NOMEM;
    }

    switch (pPdu->choice)
    {
    case MltmdSystmCntrlMssg_rqst_chosen:
        pOut->u.indication.u.functionNotUnderstood.choice = FnctnNtUndrstd_request_chosen;
        pOut->u.indication.u.functionNotUnderstood.u.FnctnNtUndrstd_request =
          pPdu->u.MltmdSystmCntrlMssg_rqst;
        break;

    case MSCMg_rspns_chosen:
        pOut->u.indication.u.functionNotUnderstood.choice = FnctnNtUndrstd_response_chosen;
        pOut->u.indication.u.functionNotUnderstood.u.FnctnNtUndrstd_response =
          pPdu->u.MSCMg_rspns;
        break;

    case MSCMg_cmmnd_chosen:
        pOut->u.indication.u.functionNotUnderstood.choice = FnctnNtUndrstd_command_chosen;
        pOut->u.indication.u.functionNotUnderstood.u.FnctnNtUndrstd_command =
          pPdu->u.MSCMg_cmmnd;
        break;

    default:
        // Can't reply to unsupported indication...
        MemFree(pOut);
        return H245_ERROR_OK;
    } // switch (Type)

    pOut->choice = indication_chosen;
    pOut->u.indication.choice = functionNotUnderstood_chosen;
    lError = sendPDU(pInstance, pOut);
    MemFree(pOut);
    return lError;
} // SendFunctionNotUnderstood()



/*
 *  NAME
 *      FsmOutgoing - process outbound PDU
 *
 *
 *  PARAMETERS
 *      INPUT   pInst       Pointer to FSM instance structure
 *      INPUT   pPdu        Pointer to PDU to send
 *      INPUT   dwTransId   Transaction identifier to use for response
 *
 *  RETURN VALUE
 *      Error codes defined in h245com.h
 */

HRESULT
FsmOutgoing(struct InstanceStruct *pInstance, PDU_t *pPdu, DWORD_PTR dwTransId)
{
    HRESULT             lError;
    Entity_t            Entity;
    Event_t             Event;
    Key_t               Key;
    int                 bCreate;
    Object_t *          pObject;

    ASSERT(pInstance != NULL);
    ASSERT(pPdu != NULL);
    H245TRACE(pInstance->dwInst, 4, "FsmOutgoing");

#if defined(_DEBUG)
    if (check_pdu(pInstance, pPdu))
      return H245_ERROR_ASN1;
#endif // (DEBUG)

    lError = PduParseOutgoing(pInstance, pPdu, &Entity, &Event, &Key, &bCreate);
    if (lError != H245_ERROR_OK)
    {
        H245TRACE(pInstance->dwInst, 1,
          "FsmOutgoing: PDU not recognized; Error=%d", lError);
        return lError;
    }

    ASSERT(Entity < NUM_ENTITYS);

    if (Entity == STATELESS)
    {
        H245TRACE(pInstance->dwInst, 4, "FsmOutgoing: Sending stateless PDU");
        return sendPDU(pInstance, pPdu);
    }

    ASSERT(Event < NUM_STATE_EVENTS);

    pObject = ObjectFind(pInstance, Entity, Key);
    if (pObject == NULL)
    {
        if (bCreate == FALSE)
        {
#if defined(_DEBUG)
            H245TRACE(pInstance->dwInst, 1,
                      "FsmOutgoing: State Entity %s(%d) not found; Key=%d",
                      EntityName[Entity], Entity, Key);
#else
            H245TRACE(pInstance->dwInst, 1,
                      "FsmOutgoing: State Entity %d not found; Key=%d",
                      Entity, Key);
#endif
            return H245_ERROR_PARAM;
        }
        pObject = ObjectCreate(pInstance, Entity, Key, dwTransId);
        if (pObject == NULL)
        {
            H245TRACE(pInstance->dwInst, 1, "FsmOutgoing: State Entity memory allocation failed");
            return H245_ERROR_NOMEM;
        }
    }
    else
    {
        pObject->dwTransId = dwTransId;
    }

    return StateMachine(pObject, pPdu, Event);
} // FsmOutgoing()



/*
 *  NAME
 *      FsmIncoming - process inbound PDU
 *
 *
 *  PARAMETERS
 *      INPUT   dwInst      current H.245 instance
 *      INPUT   pPdu        pointer to a PDU structure
 *
 *  RETURN VALUE
 *      error codes defined in h245com.h (not checked)
 */

HRESULT
FsmIncoming(struct InstanceStruct *pInstance, PDU_t *pPdu)
{
    HRESULT             lError;
    Entity_t            Entity;
    Event_t             Event;
    Key_t               Key;
    int                 bCreate;
    Object_t *          pObject;
    Object_t *          pObject1;

    ASSERT(pInstance != NULL);
    ASSERT(pPdu != NULL);
    H245TRACE(pInstance->dwInst, 4, "FsmIncoming");

    lError = PduParseIncoming(pInstance, pPdu, &Entity, &Event, &Key, &bCreate);
    if (lError != H245_ERROR_OK)
    {
        H245TRACE(pInstance->dwInst, 1,
          "FsmIncoming: Received PDU not recognized", lError);
        SendFunctionNotUnderstood(pInstance, pPdu);
        return lError;
    }

    ASSERT(Entity < NUM_ENTITYS);

    if (Entity == STATELESS)
    {
        H245TRACE(pInstance->dwInst, 4, "FsmIncoming: Received stateless PDU");
        return H245FsmIndication(pPdu, (DWORD)StatelessTable[Event - NUM_STATE_EVENTS], pInstance, 0, H245_ERROR_OK);
    }

    ASSERT(Event < NUM_STATE_EVENTS);

    if (Event == MaintenanceLoopOffCommandPDU)
    {
        // Special case MaintenanceLoopOff applies to ALL loops
        ASSERT(Entity == MLSE_IN);
        pObject = pInstance->StateMachine.Object_tbl[Entity];
        if (pObject == NULL)
        {
            return H245_ERROR_OK;
        }
        lError = StateMachine(pObject, pPdu, Event);
        pObject = pInstance->StateMachine.Object_tbl[Entity];
        while (pObject)
        {
            if (pObject->uNestLevel == 0)
            {
                pObject1 = pObject;
                pObject  = pObject->pNext;
                ObjectDestroy(pObject1);
            }
            else
            {
                pObject->State = 0;
                pObject = pObject->pNext;
            }
        }
        return lError;
    } // if

    pObject = ObjectFind(pInstance, Entity, Key);
    if (pObject == NULL)

    {
        if (bCreate == FALSE)
        {
#if defined(_DEBUG)
            H245TRACE(pInstance->dwInst, 1,
                      "FsmIncoming: State Entity %s(%d) not found; Key=%d",
                      EntityName[Entity], Entity, Key);
#else
            H245TRACE(pInstance->dwInst, 1,
                      "FsmIncoming: State Entity %d not found; Key=%d",
                      Entity, Key);
#endif
            return H245_ERROR_PARAM;
        }
        pObject = ObjectCreate(pInstance, Entity, Key, 0);
        if (pObject == NULL)
        {
            H245TRACE(pInstance->dwInst, 1, "FsmIncoming: State Entity memory allocation failed");
            return H245_ERROR_NOMEM;
        }
    }

    return StateMachine(pObject, pPdu, Event);
} // FsmIncoming()


// CAVEAT: Need to save dwInst since StateMachine() might deallocate pObject!
HRESULT
FsmTimerEvent(struct InstanceStruct *pInstance, DWORD_PTR dwTimerId, Object_t *pObject, Event_t Event)
{
    ASSERT(pInstance != NULL);
    ASSERT(pObject   != NULL);
    ASSERT(pObject->pInstance == pInstance);
    ASSERT(pObject->dwTimerId == dwTimerId);
    H245TRACE(pInstance->dwInst, 4, "FsmTimerEvent");
    pObject->dwTimerId = 0;
    return StateMachine(pObject, NULL, Event);
} // FsmTimerEvent()