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

 @(#) $Id: sls_tcbc.h,v 0.7.2.9 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_TCBC_H__
#define __SLS_TCBC_H__

/*
 *  Referenced functions.
 *
 *  This state machine accesses HMRT and routing functions.
 */

static inline void lk_tsrc_tcbc_alternative_routing_data_request(lk_t *lk);
static inline void lk_tsrc_tcbc_procedure_terminated(lk_t *lk);

/* link and linkset functions */

static inline void lk_hmrt_tcbc_changeback_ack(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcbc_changeback_declaration(lk_t *lk, mblk_t *mp);
static inline void lk_hmrt_tcbc_update_routing_tables(lk_t *lk);

static inline void lk_tlac_tcbc_changeback_terminated(lk_t *lk);

/*
 *  Forward declarations.
 */

static inline void lk_t3_timeout(lk_t *lk);
static inline void lk_t4_timeout(lk_t *lk);
static inline void lk_t5_timeout(lk_t *lk);

static inline void lk_tcbc_tsrc_alternative_routing_data(lk_t *lk); /* from MTP */
static inline void lk_tcbc_hmrt_changeback_declaration(lk_t *lk, mblk_t *mp);   /* from remote L3 */
static inline void lk_tcbc_hmrt_changeback_ack(lk_t *lk, mblk_t *mp);           /* from remote L3 */
static inline void lk_tcbc_tlac_signalling_link_available(lk_t *lk);

/*
 *  Message building functions
 */

static inline void lk_tcbc_send_changeback_declaration(lk_t *lk, mblk_t *mp)
{
    ls_t *ls;
    ls_prim_t *p;
    mp = __msg_alloc(mp);
    ls = lk->module;
    p = (ls_prim_t *)mp->b_rptr;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_CBD;
    p->cbd.mh.ni     = ls->config.ni;
    p->cbd.mh.mp     = 0x3;
    p->cbd.mh.si     = 0x2;
    p->cbd.mh.rl.dpc = ls->config.apc;
    p->cbd.mh.rl.opc = ls->config.lpc;
    p->cbd.mh.rl.sls = lk->config.slc;
    p->cbd.slc       = lk->config.slc;
    p->cbd.cbc       = lk->statem.cbc;  /* FIXME */
    lk_hmrt_tcbc_changeback_declaration(lk, mp);
}

static inline void lk_tcbc_send_changeback_ack(lk_t *lk, mblk_t *mp)
{
    ls_t *ls = lk->module;
    ls_prim_t *p = (ls_prim_t *)mp->b_rptr;
    if ( !mp ) return;
    p->primitive     = LS_MESSAGE_FOR_ROUTING_IND;
    p->sig.signal    = LS_SIGNAL_CBA;
    p->cba.mh.rl.dpc = ls->config.apc;
    p->cba.mh.rl.opc = ls->config.lpc;
    lk_hmrt_tcbc_changeback_ack(lk, mp);
}

/*
 *  Signalling Traffic Management (STM)
 *  Changeback Control (TCBC)
 *  Figure 31/Q.704
 */

static inline void lk_t3_timeout(lk_t *lk)
{
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_TIME_CONTROLLED_DIVERSION:
            /* Restart traffic on link buffered messages first */
            lk_hmrt_tcbc_update_routing_tables(lk);
            lk_tlac_tcbc_changeback_terminated(lk);
            lk_tsrc_tcbc_procedure_terminated(lk);
            lk->statem.tcbc_state = LK_STATE_IDLE;
            break;
    }
}

static inline void lk_t4_timeout(lk_t *lk)
{
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_FIRST_ATTEMPT:
            lk_tcbc_send_changeback_declaration(lk, NULL);
            lk_timer_start(t5);
            lk->statem.tcbc_state = LK_STATE_SECOND_ATTEMPT;
            break;
    }
}

static inline void lk_t5_timeout(lk_t *lk)
{
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_SECOND_ATTEMPT:
            /* Restart traffic on link buffered messages first */
            lk_hmrt_tcbc_update_routing_tables(lk);
            lk_tlac_tcbc_changeback_terminated(lk);
            lk_tsrc_tcbc_procedure_terminated(lk);
            lk->statem.tcbc_state = LK_STATE_IDLE;
            break;
    }
}

static inline void lk_tcbc_tlac_signalling_link_available(lk_t *lk)
{
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_IDLE:
            lk_tsrc_tcbc_alternative_routing_data_request(lk);
            lk->statem.tcbc_state = LK_STATE_WAIT_FOR_ROUTING_DATA;
            break;
    }
}

static inline void lk_tcbc_hmrt_changeback_declaration(lk_t *lk, mblk_t *mp) /* from remote L3 */
{
    /* With changeback code x (from remote level 3) */
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_IDLE:
            lk_tcbc_send_changeback_ack(lk, mp); /* with changeback code x (to remote level 3) */
            break;
        case LK_STATE_WAIT_FOR_ROUTING_DATA:
        case LK_STATE_TIME_CONTROLLED_DIVERSION:
        case LK_STATE_FIRST_ATTEMPT:
        case LK_STATE_SECOND_ATTEMPT:
            freemsg(mp);
            break;
    }
}

static inline void lk_tcbc_tsrc_alternative_routing_data(lk_t *lk)
{
    /* Identifies alternative link(s) carrying traffic to be changed back and
     * also identifies routing and SP accessibility changes */
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_WAIT_FOR_ROUTING_DATA:
            /*
             *  This routing_changed check is to see whether
             *  traffic will be applied to the link once the
             *  changeback is complete.  If no traffic is to
             *  be applied to the link, time controlled
             *  diversion is not necessary.
             */
            if ( lk->statem.routing_changed ) {
                /* Buffer concerned messages in changeback buffer(s) */
                if ( lk->statem.remote_sp_accessible ) {
                    /* Is SP at remote end of newly available link accessible
                     * via alternative link? */
                    lk_tcbc_send_changeback_declaration(lk, NULL); /* Send to above SP
                                                           via alternative
                                                           link with
                                                           changeback code y
                                                           (to remote level 3)
                                                         */
                    lk_timer_start(t4);
                    lk->statem.tcbc_state = LK_STATE_FIRST_ATTEMPT;
                } else {
                    lk_timer_start(t3);
                    lk->statem.tcbc_state = LK_STATE_TIME_CONTROLLED_DIVERSION;
                    /* Wait to reduce risk that diversion causes
                     * mis-sequencing */
                }
            } else {
                lk_tlac_tcbc_changeback_terminated(lk);
                lk_tsrc_tcbc_procedure_terminated(lk);
                lk->statem.tcbc_state = LK_STATE_IDLE;
            }
            break;
    }
}

static inline void lk_tcbc_hmrt_changeback_ack(lk_t *lk, mblk_t *mp) /* from remote L3 */
{
    switch ( lk->statem.tcbc_state ) {
        case LK_STATE_FIRST_ATTEMPT:
            lk_timer_stop(t4);
            /* Restart traffic on link buffered messages first */
            lk_hmrt_tcbc_update_routing_tables(lk);
            lk_tlac_tcbc_changeback_terminated(lk);
            lk_tsrc_tcbc_procedure_terminated(lk);
            lk->statem.tcbc_state = LK_STATE_IDLE;
            break;
        case LK_STATE_SECOND_ATTEMPT:
            lk_timer_stop(t5);
            /* Restart traffic on link buffered messages first */
            lk_hmrt_tcbc_update_routing_tables(lk);
            lk_tlac_tcbc_changeback_terminated(lk);
            lk_tsrc_tcbc_procedure_terminated(lk);
            lk->statem.tcbc_state = LK_STATE_IDLE;
            break;
    }
    freemsg(mp);
}

#endif  __SLS_TCBC_H__
