/*****************************************************************************

 @(#) $Id: sls_tcoc.h,v 0.7.2.8 2001/02/03 05:48:54 brian Test $

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified $Date: 2001/02/03 05:48:54 $ by $Author: brian $

 *****************************************************************************/

#ifndef __SLS_TCOC_H__
#define __SLS_TCOC_H__

/*
 *  References to other functions.
 *
 *  This state machine accesses HMRT and routing functions.
 */

static inline void lk_tsrc_tcoc_alternative_routing_data_request(lk_t *lk);
static inline void lk_tsrc_tcoc_changeover_executed(lk_t *lk);
static inline void lk_tsrc_tcoc_changeover_not_required(lk_t *lk);

/* link and linkset functions */

static inline void lk_hmrt_tcoc_changeover_ack(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcoc_changeover_order(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcoc_emergency_changeover_ack(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcoc_emergency_changeover_order(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcoc_update_routing_tables(lk_t *lk);

static inline void lk_lsac_tcoc_STM_ready(lk_t *lk);

static inline void lk_tlac_tcoc_changeover_executed(lk_t *lk);
static inline void lk_tlac_tcoc_changeover_not_required(lk_t *lk);

/*
 *  Forward declarations.
 */

static inline void lk_t1_timeout(lk_t *lk);
static inline void lk_t2_timeout(lk_t *lk);

static inline void lk_tcoc_tsrc_alternative_routine_data(lk_t *lk);   /* from MTP */
static inline void lk_tcoc_hmdt_changeover_ack(lk_t *lk, mblk_t *mp);           /* from remote L3 */
static inline void lk_tcoc_hmdt_emergency_changeover_ack(lk_t *lk, mblk_t *mp); /* from remote L3 */
static inline void lk_tcoc_tlac_changeover_order(lk_t *lk, int fsnc);
static inline void lk_tcoc_tlac_emergency_changeover_order(lk_t *lk);
static inline void lk_tcoc_tlac_signalling_link_available(lk_t *lk);
static inline void lk_tcoc_tlac_signalling_link_unavailable(lk_t *lk);
static inline void lk_tcoc_l2_bsnt(lk_t *lk, int bsnt);                 /* from L2 */
static inline void lk_tcoc_l2_bsnt_not_retrievable(lk_t *lk);           /* from L2 */
static inline void lk_tcoc_l2_retrieval_complete(lk_t *lk);             /* from L2 */
static inline void lk_tcoc_l2_retrieval_not_possible(lk_t *lk);         /* from L2 */
static inline void lk_tcoc_l2_retrieved_messages(lk_t *lk, mblk_t *mp); /* from L2 */

/*
 *  Message building functions.
 */

static inline void lk_tcoc_send_changeover_order(lk_t *lk)
{
    mblk_t *mp;
    ls_t *ls;
    ls_prim_t *p;
    if ( !(mp = __msg_alloc(NULL)) ) return;
    ls = lk->module;
    p = (ls_prim_t *)mp->b_rptr;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_COO;
    p->coo.mh.ni     = ls->config.ni;
    p->coo.mh.mp     = 0x3;
    p->coo.mh.si     = 0x0;
    p->coo.mh.rl.dpc = ls->config.apc;
    p->coo.mh.rl.opc = ls->config.lpc;
    p->coo.mh.rl.sls = lk->config.slc;
    p->coo.slc       = lk->config.slc;
    p->coo.fsnc      = lk->statem.bsnt;
    lk_hmrt_tcoc_changeover_order(lk, mp);
}

static inline void lk_tcoc_send_emergency_changeover_order(lk_t *lk)
{
    mblk_t *mp;
    ls_t *ls;
    ls_prim_t *p;
    if ( !(mp = __msg_alloc(NULL)) ) return;
    ls = lk->module;
    p = (ls_prim_t *)mp->b_rptr;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_ECO;
    p->eco.mh.ni     = ls->config.ni;
    p->eco.mh.mp     = 0x3;
    p->eco.mh.si     = 0x0;
    p->eco.mh.rl.dpc = ls->config.apc;
    p->eco.mh.rl.opc = ls->config.lpc;
    p->eco.mh.rl.sls = lk->config.slc;
    p->eco.slc       = lk->config.slc;
    lk_hmrt_tcoc_emergency_changeover_order(lk, mp);
}

static inline void lk_tcoc_send_changeover_ack(lk_t *lk)
{
    mblk_t *mp;
    ls_t *ls;
    ls_prim_t *p;
    if ( !(mp = __msg_alloc(NULL)) ) return;
    ls = lk->module;
    p = (ls_prim_t *)mp->b_rptr;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_COA;
    p->coa.mh.ni     = ls->config.ni;
    p->coa.mh.mp     = 0x3;
    p->coa.mh.si     = 0x0;
    p->coa.mh.rl.dpc = ls->config.apc;
    p->coa.mh.rl.opc = ls->config.lpc;
    p->coa.mh.rl.sls = lk->config.slc;
    p->coa.slc       = lk->config.slc;
    p->coa.fsnc      = lk->statem.bsnt;
    lk_hmrt_tcoc_changeover_ack(lk, mp);
}

static inline void lk_tcoc_send_emergency_changeover_ack(lk_t *lk)
{
    mblk_t *mp;
    ls_t *ls;
    ls_prim_t *p;
    if ( !(mp = __msg_alloc(NULL)) ) return;
    ls = lk->module;
    p = (ls_prim_t *)mp->b_rptr;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_ECA;
    p->eca.mh.ni     = ls->config.ni;
    p->eca.mh.mp     = 0x3;
    p->eca.mh.si     = 0x0;
    p->eca.mh.rl.dpc = ls->config.apc;
    p->eca.mh.rl.opc = ls->config.lpc;
    p->eca.mh.rl.sls = lk->config.slc;
    p->eca.slc       = lk->config.slc;
    lk_hmrt_tcoc_emergency_changeover_ack(lk, mp);
}

/*
 *  Timer functions.
 */

static inline void lk_t1_timeout(lk_t *lk)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT07:
            /* send buffered messages on alternative signalling link(s);
             * messages for inaccessible destinations are discarded. */
            lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that subsequent
                                                  messages for unavailable
                                                  link are diverted to
                                                  alternative link(s) (if any)
                                                  or discarded */
            lk_tlac_tcoc_changeover_executed(lk);
            lk_tsrc_tcoc_changeover_executed(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
    }
}

static inline void lk_t2_timeout(lk_t *lk)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT:
            lk->dcalls->sl_stop(lk);
            lk_lsac_tcoc_STM_ready(lk); /* This message is required only if
                                      signalling link restoration may
                                      interfere with message retrieval. */
            /* Send buffered messages on alternative signalling link(s);
             * messages for inaccessible destinations are discarded. */
            lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that subsequent
                                                  messages for unavailable
                                                  link are diverted to
                                                  alternative link(s) (if any)
                                                  or discarded. */
            lk_tlac_tcoc_changeover_executed(lk);
            lk_tsrc_tcoc_changeover_executed(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
    }
}

static inline void lk_tcoc_tlac_signalling_link_unavailable(lk_t *lk)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_IDLE:
            /* buffer messages for affected signalling link */
            lk_tsrc_tcoc_alternative_routing_data_request(lk);
            lk->statem.tcoc_state = LK_STATE_WAIT01;
            break;
        case LK_STATE_RETRIEVING:
            lk->statem.unavailable = 1;
            break;
    }
}

static inline void lk_tcoc_tsrc_alternative_routine_data(lk_t *lk)
{
    /*
     *  Identifies alternative signalling links, if any and also identifies
     *  whether unavailable link was loaded and accessibility of other SP.
     */
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT01:
            if ( lk->statem.retrieval_required ) { /* retrieval required? */
                if ( lk->statem.changeover_possible ) { /* can changeover messages be exchanged? */
                    lk->dcalls->sl_bsnt(lk);
                    lk->statem.tcoc_state = LK_STATE_WAIT02;
                } else {
                    /* For inhibiting of link or other SP might be
                     * inaccessible.  NOTE - In the processor outage and
                     * management inhibiting states, changeover messages
                     * should not be exchanged. */
                    lk_lsac_tcoc_STM_ready(lk); /* This message is required only if
                                              signalling link restoration
                                              attempts may interfere with
                                              message retrieval. */
                    lk_timer_start(t1);
                    lk->statem.tcoc_state = LK_STATE_WAIT07; /* To reduce risk
                                                                that diversion
                                                                will cause
                                                                mis-sequencing.
                                                              */
                }
            } else {
                lk->dcalls->sl_fsnc(lk, lk->statem.fsnc); /* FIXME: where is the FSNC? */
                lk->statem.unavailable = 1;
                lk->statem.tcoc_state = LK_STATE_RETRIEVING;
            }
            break;
        case LK_STATE_WAIT06:
            if ( lk->statem.retrieval_required && lk->statem.remote_bsnt_retrieved ) {
                lk->dcalls->sl_fsnc(lk, lk->statem.fsnc); /* use remote bsnt */
                lk->statem.unavailable = 1;
                lk->statem.tcoc_state = LK_STATE_RETRIEVING;
            } else {
                lk_lsac_tcoc_STM_ready(lk); /* This message is required only if
                                          signalling link restoration may
                                          interfere with message retrieval. */
                /* Send buffered messages on alternative signalling link(s);
                 * messages for inaccessible destinations are discarded. */
                lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that
                                                      subsequent messages for
                                                      unavailable link are
                                                      diverted to alternative
                                                      link(s) (if any) or
                                                      discarded. */
                lk_tlac_tcoc_changeover_executed(lk);
                lk_tsrc_tcoc_changeover_executed(lk);
                lk->statem.tcoc_state = LK_STATE_IDLE;
            }
            break;
    }
}

static inline void lk_tcoc_tlac_emergency_changeover_order(lk_t *lk)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_IDLE:
            lk->statem.fsnc = 0;
            lk->statem.remote_bsnt_retrieved = 0;
            /* Buffer messages for the affected signalling link. */
            lk->dcalls->sl_bsnt(lk);   /* Needed for updating retransmission
                                          buffer at remote end. */
            lk->statem.tcoc_state = LK_STATE_WAIT05;
            break;
        case LK_STATE_WAIT01:
        case LK_STATE_WAIT02:
            break;
    }
}

static inline void lk_tcoc_tlac_changeover_order(lk_t *lk, int fsnc)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_IDLE:
            lk->statem.fsnc = fsnc;
            lk->statem.remote_bsnt_retrieved = 1;
            /* Buffer messags for the affected signalling link. */
            lk->dcalls->sl_bsnt(lk);   /* Needed for updating retransmission
                                          buffer at remote end. */
            lk->statem.tcoc_state = LK_STATE_WAIT05;
            break;
        case LK_STATE_WAIT01:
        case LK_STATE_WAIT02:
            break;
        case LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT:
            lk_timer_stop(t2);
            if ( lk->statem.local_bsnt_retrieved )
                lk_tcoc_send_changeover_ack(lk);
            else
                lk_tcoc_send_emergency_changeover_ack(lk);
            lk->dcalls->sl_fsnc(lk, fsnc);
            lk->statem.unavailable = 1;
            lk->statem.tcoc_state = LK_STATE_RETRIEVING;
            break;
    }
}

static inline void lk_tcoc_tlac_signalling_link_available(lk_t *lk)
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT07:   /* To reduces the risk that diversion will
                                   cause mis-sequencing. */
            lk_timer_stop(t1);
            /* fall thru */
        case LK_STATE_WAIT01:
        case LK_STATE_WAIT02:
            /* send buffered messages on restored link */
            /* stop buffering messages for restored link */
            lk_tlac_tcoc_changeover_not_required(lk);
            lk_tsrc_tcoc_changeover_not_required(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
        case LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT:
            lk_timer_stop(t2);
            if ( lk->statem.local_bsnt_retrieved )
                lk_tcoc_send_changeover_ack(lk);
            else
                lk_tcoc_send_emergency_changeover_ack(lk);
            lk_lsac_tcoc_STM_ready(lk); /* This message is required only if
                                      signalling link restoration may
                                      interfere with message retrieval. */
            /* Send buffered messages on alternative signalling link(s);
             * messages for inaccessible destinations are discarded. */
            lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that subsequent
                                                  messages for unavailable
                                                  link are diverted to
                                                  alternative link(s) (if any)
                                                  or discarded. */
            lk_tlac_tcoc_changeover_executed(lk);
            lk_tsrc_tcoc_changeover_executed(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
        case LK_STATE_RETRIEVING:
            lk->statem.unavailable = 0;
            break;
        case LK_STATE_WAIT06:
            /* Send buffered messages on restored link. */
            /* Stop buffering messages for restored link. */
            lk_tlac_tcoc_changeover_not_required(lk);
            lk_tsrc_tcoc_changeover_not_required(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
    }
}

static inline void lk_tcoc_l2_bsnt(lk_t *lk, int bsnt)   /* bsnt from L2 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT02:
            lk->statem.bsnt = bsnt;
            lk->statem.local_bsnt_retrieved = 1;
            lk_tcoc_send_changeover_order(lk);
            lk_timer_start(t2);
            lk->statem.tcoc_state = LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT;
            break;
        case LK_STATE_WAIT05:
            lk_tcoc_send_changeover_ack(lk); /* use retrieved bnst */
            lk_tsrc_tcoc_alternative_routing_data_request(lk);
            lk->statem.tcoc_state = LK_STATE_WAIT06;
            break;
    }
}

static inline void lk_tcoc_l2_bsnt_not_retrievable(lk_t *lk) /* unspec from L2 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT02:
            lk->statem.bsnt = 0;
            lk->statem.local_bsnt_retrieved = 0;
            lk->dcalls->sl_stop(lk);
            lk_tcoc_send_emergency_changeover_order(lk);
            lk_timer_start(t2);
            lk->statem.tcoc_state = LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT;
            break;
        case LK_STATE_WAIT05:
            lk->dcalls->sl_stop(lk);
            lk_tcoc_send_emergency_changeover_ack(lk);
            lk_tsrc_tcoc_alternative_routing_data_request(lk);
            lk->statem.tcoc_state = LK_STATE_WAIT06;
            break;
    }
}

static inline void lk_tcoc_hmdt_changeover_ack(lk_t *lk, mblk_t *mp) /* from remote L3 */
{
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT:
            lk_timer_stop(t2);
            lk->dcalls->sl_fsnc(lk, p->coa.fsnc);    /* FSNC from message. */
            lk->statem.unavailable = 1;
            lk->statem.tcoc_state = LK_STATE_RETRIEVING;
            break;
    }
    freemsg(mp);
}

static inline void lk_tcoc_hmdt_emergency_changeover_ack(lk_t *lk, mblk_t *mp) /* from remote L3 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_WAIT_FOR_ACKNOWLEDGEMENT:
            lk_timer_stop(t2);
            lk_lsac_tcoc_STM_ready(lk);  /* This message is required only if
                                       signalling link restoration may
                                       interfere with message retrieval. */
            /* Send buffered messages on alternative signalling link(s);
             * messages for inaccessible destinations are discarded. */
            lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that subsequent
                                                  messages for unavailable
                                                  link are diverted to
                                                  alternative link(s) (if any)
                                                  or discarded. */
            lk_tlac_tcoc_changeover_executed(lk);
            lk_tsrc_tcoc_changeover_executed(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
    }
    freemsg(mp);
}

static inline void lk_tcoc_l2_retrieved_messages(lk_t *lk, mblk_t *mp) /* from L2 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_RETRIEVING:
            /* Save retrieved messages */ /* FIXME */
            break;
    }
}

static inline void lk_tcoc_l2_retrieval_complete(lk_t *lk) /* from L2 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_RETRIEVING:
            if ( lk->statem.unavailable ) {
                /* Send retrieved messages on alternative signalling link(s). */
                lk_lsac_tcoc_STM_ready(lk);  /* This message is required only if
                                           signalling link restoration may
                                           interfere with message retrieval.
                                         */
                /* Send buffered messages on alternative signalling link(s);
                 * messages for inaccessible destinations are discarded. */
                lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that
                                                      subsequent messages for
                                                      unavailable link are
                                                      diverted to alternative
                                                      link(s) (if any) or
                                                      discarded. */
                lk_tlac_tcoc_changeover_executed(lk);
                lk_tsrc_tcoc_changeover_executed(lk);
                lk->statem.tcoc_state = LK_STATE_IDLE;
                break;
            } else {
                /* Send retrieved messages on restored link. */
                /* Send buffered messages on restored link. */
                /* Stop buffering messages for restored link. */
                lk_tlac_tcoc_changeover_not_required(lk);
                lk_tsrc_tcoc_changeover_not_required(lk);
                lk->statem.tcoc_state = LK_STATE_IDLE;
            }
        break;
    }
}

static inline void lk_tcoc_l2_retrieval_not_possible(lk_t *lk) /* uspec from L2 */
{
    switch ( lk->statem.tcoc_state ) {
        case LK_STATE_RETRIEVING:
            lk_lsac_tcoc_STM_ready(lk);  /* This message is required only if
                                       signalling link restoration may
                                       interfere with message retrieval. */
            /* Send buffered messages on alternative signalling link(s);
             * messages for inaccessible destinations are discarded. */
            lk_hmrt_tcoc_update_routing_tables(lk); /* To ensure that subsequent
                                                  messages for unavailable
                                                  link are diverted to
                                                  alternative link(s) (if any)
                                                  or discarded. */
            lk_tlac_tcoc_changeover_executed(lk);
            lk_tsrc_tcoc_changeover_executed(lk);
            lk->statem.tcoc_state = LK_STATE_IDLE;
            break;
    }
}

#endif  __SLS_TCOC_H__

