OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/slsi/sls_sm.h


File /code/strss7/drivers/slsi/sls_sm.h



#ifndef __SLS_LK_SM_H__
#define __SLS_LK_SM_H__

#ident "@(#) $RCSfile: sls_sm.h,v $ $Name:  $($Revision: 0.8.2.1 $) $Date: 2002/10/18 02:40:38 $"

/*
 *  This file provides procedures for the MTP Level 3 Link Set.  Link set procedures
 *  are restricted to things involving links and link sets.
 */

#define LS_SLS_TYPE_ITUT    0
#define LS_SLS_TYPE_ANSI    1
#define LS_SLS_TYPES        0x00000010f0

/*
 *  RT->LS
 */

static inline void ls_message_from_routing(ls_t * ls, mblk_t * mp);
static inline void ls_message_for_distribution(ls_t * ls, mblk_t * mp);
static inline void ls_message_for_rerouting(ls_t * ls, mblk_t * mp);
static inline void ls_link_activate(ls_t * ls, int slc);
static inline void ls_link_deactivate(ls_t * ls, int slc);
static inline void ls_link_inhibit(ls_t * ls, int slc);
static inline void ls_link_uninhibit(ls_t * ls, int slc);
static inline void ls_activate(ls_t * ls);
static inline void ls_deactivate(ls_t * ls);
static inline void ls_inhibit(ls_t * ls);
static inline void ls_uninhibit(ls_t * ls);
static inline void ls_force_uninhibit(ls_t * ls);
static inline void ls_routing_outage(ls_t * ls);
static inline void ls_routing_recovered(ls_t * ls);
static inline void ls_critical(ls_t * ls);
static inline void ls_noncritical(ls_t * ls);
static inline void ls_emergency(ls_t * ls);
static inline void ls_emergency_ceases(ls_t * ls);
static inline void ls_traffic_starts(ls_t * ls);
static inline void ls_traffic_ends(ls_t * ls);
static inline void ls_adjacent_sp_inaccessible(ls_t * ls);
static inline void ls_adjacent_sp_accessible(ls_t * ls);

/*
 *  SL->LK
 */

static inline void sl_pdu(lk_t * lk, mblk_t * mp);
static inline void sl_link_congested(lk_t * lk, int cong, int disc);
static inline void sl_link_congestion_ceased(lk_t * lk, int cong, int disc);
static inline void sl_retrieved_message(lk_t * lk, mblk_t * mp);
static inline void sl_retrieval_complete(lk_t * lk);
static inline void sl_retrieval_not_possible(lk_t * lk);
static inline void sl_rb_cleared(lk_t * lk);
static inline void sl_bsnt(lk_t * lk, int bsnt);
static inline void sl_bsnt_not_retrievable(lk_t * lk);
static inline void sl_in_service(lk_t * lk);
static inline void sl_out_of_service(lk_t * lk, int reason);
static inline void sl_remote_processor_outage(lk_t * lk);
static inline void sl_remote_processor_recovered(lk_t * lk);
static inline void sl_rtb_cleared(lk_t * lk);

/*
 *  Timeouts
 */

static void lk_t1_timeout(lk_t * lk);
static void lk_t2_timeout(lk_t * lk);
static void lk_t12_timeout(lk_t * lk);
static void lk_t13_timeout(lk_t * lk);
static void lk_t14_timeout(lk_t * lk);
static void lk_t17_timeout(lk_t * lk);
static void lk_t22_timeout(lk_t * lk);
static void lk_t23_timeout(lk_t * lk);

/*
 *  Messages
 */

static inline void lk_coo(lk_t * lk, mblk_t * mp);
static inline void lk_coa(lk_t * lk, mblk_t * mp);
static inline void ls_cbd(ls_t * ls, mblk_t * mp);
static inline void ls_cba(ls_t * ls, mblk_t * mp);
static inline void lk_eco(lk_t * lk, mblk_t * mp);
static inline void lk_eca(lk_t * lk, mblk_t * mp);
static inline void lk_dlc(lk_t * lk, mblk_t * mp);
static inline void lk_css(lk_t * lk, mblk_t * mp);
static inline void lk_cns(lk_t * lk, mblk_t * mp);
static inline void lk_cnp(lk_t * lk, mblk_t * mp);
static inline void lk_lin(lk_t * lk, mblk_t * mp);
static inline void lk_lun(lk_t * lk, mblk_t * mp);
static inline void lk_lia(lk_t * lk, mblk_t * mp);
static inline void lk_lua(lk_t * lk, mblk_t * mp);
static inline void lk_lid(lk_t * lk, mblk_t * mp);
static inline void lk_lfu(lk_t * lk, mblk_t * mp);
static inline void lk_lli(lk_t * lk, mblk_t * mp);
static inline void lk_lri(lk_t * lk, mblk_t * mp);
static inline void lk_slta(lk_t * lk, mblk_t * mp);
static inline void lk_sltm(lk_t * lk, mblk_t * mp);
static inline void lk_sslta(lk_t * lk, mblk_t * mp);
static inline void lk_ssltm(lk_t * lk, mblk_t * mp);

/*
 *  Internal and utility functions
 */

static inline void lk_send_coo(lk_t * lk, mblk_t * mp);
static inline void lk_send_lfu(lk_t * lk, mblk_t * mp);
static inline void lk_send_lua(lk_t * lk, mblk_t * mp);
static inline void lk_send_lli(lk_t * lk, mblk_t * mp);
static inline void lk_send_lri(lk_t * lk, mblk_t * mp);

static inline void lk_activated(lk_t * lk);
static inline void lk_available(lk_t * lk);
static inline void lk_local_blocked(lk_t * lk);
static inline void lk_remote_blocked(lk_t * lk);
static inline void lk_changeover_complete(lk_t * lk);
static inline void lk_deactivated(lk_t * lk);
static inline void lk_failed(lk_t * lk);
static inline void lk_local_inhibited(lk_t * lk);
static inline void lk_remote_inhibited(lk_t * lk);
static inline void lk_restored(lk_t * lk);
static inline void lk_unavailable(lk_t * lk);
static inline void lk_local_unblocked(lk_t * lk);
static inline void lk_remote_unblocked(lk_t * lk);
static inline void lk_local_uninhibited(lk_t * lk);
static inline void lk_remote_uninhibited(lk_t * lk);
static inline void lk_update_buffer(lk_t * lk);

/*
 * --------------------------------------------------------------------------
 *
 *  Basic stuff
 *
 * --------------------------------------------------------------------------
 */

#define lk_timer_stop(tim) \
{ \
    if ( lk->timers.tim) \
        untimeout(lk->timers.tim); \
}

#define lk_timer_start(tim) \
{ \
    lk_timer_stop(tim); \
    lk->timers.tim= \
      timeout((timo_fcn_t *)lk_ ## tim ## _timeout,(caddr_t)lk,lk->config.tim); \
}

#define ls_timer_stop(tim) \
{ \
    if ( ls->timers.tim) \
        untimeout(ls->timers.tim); \
}

#define ls_timer_start(tim) \
{ \
    ls_timer_stop(tim); \
    ls->timers.tim= \
      timeout((timo_fcn_t *)ls_ ## tim ## _timeout,(caddr_t)ls,ls->config.tim); \
}

/*
 *  Sending messages, allocate or reused an mblk of the appropriate size...
 */

static mblk_t *reallocb(mblk_t * mp, int type, size_t size, int prio)
{
	mblk_t *mr;

	if ((mr = mp)) {
		mblk_t *md = unlinkb(mp);
		if (md)
			freemsg(md);
		if (mr->b_datap->db_base + size > mr->b_datap->db_lim) {
			freemsg(mp);
			mr = NULL;
		} else
			mr->b_rptr = mr->b_datap->db_base;
	}
	if (!mr) {
		if ((mr = allocb(size, prio))) {
			mr->b_datap->db_type = type;
			mr->b_wptr = mr->b_rptr + size;
		}
	}
	if (mr) {
		mr->b_datap->db_type = type;
		mr->b_wptr = mr->b_rptr + size;
	}
	return mr;
}

/*
 *  Link Selection Notes:
 *
 *  For all Signalling Link Management messages, the message is sent over an
 *  available link not equal to SLC.
 *
 *  For all Signalling Traffic Management messages (other than changeback
 *  messages), the message is sent on an available link not equal to SLC.
 *
 *  Changeback declarations are sent over the link identified by the SLC and
 *  the changeback code.  Changeback acknowledgements are load shared.
 *  
 *  Signalling link test messages are sent on the link identified by the SLC.
 */

static inline void ls_message_for_routing(ls_t * ls, mblk_t * mp)
{
	unsigned long *g = &ls->statem.flags;

	if (*g & LS_FLAGS_ROUTING_OUTAGE)
		/* can't get to routing, try to route internal */
		ls_message_from_routing(ls, mp);
	else
		ls->ucalls->ls_msg_for_routing(ls, mp);
}

static void lk_message_for_routing(lk_t * lk, mblk_t * mp);

/*
 *  LS_MESSAGE_FOR_ROUTING_IND:- This is the main Signalling Message Handling
 *  (SMH) function for the link and link set state machines.
 */
static inline void lk_send(lk_t * lk, mblk_t * mp, ls_ulong signal)
{
	ls_t *ls = lk->module;
	ls_prim_t *p = (ls_prim_t *) mp->b_rptr;
	int slc = lk->statem.slc;
	unsigned long *f = &lk->statem.flags;
	unsigned long *g = &ls->statem.flags;

	if (!mp)
		return;

	p->primitive = LS_MESSAGE_FOR_ROUTING_IND;
	p->slm.signal = signal;

	p->slm.mh.ni = ls->config.ni;
	p->slm.mh.rl.dpc = ls->config.apc;
	p->slm.mh.rl.opc = ls->config.lpc;
	p->slm.mh.rl.sls = slc;	/* should be link actually sent on */
	p->slm.slc = slc;
	switch (p->sig.signal) {
		/* 
		 *  LUN and LUA are sent on the inhibited signalling link (the link to
		 *  which they apply) if there is no other available signalling link.
		 *  It is a question whether to send the message on a blocked link.
		 *
		 *  Otherwise, and as with LFU, CBD, SLTM, SLTA, SSLTM and SSLTA, the
		 *  message is sent only on the signalling link to which it applies.
		 */
	case LS_SIGNAL_LUN:
	case LS_SIGNAL_LUA:
		if (!(*g & LS_FLAGS_ADJ_INACCESSIBLE))
			break;
	case LS_SIGNAL_LFU:
	case LS_SIGNAL_CBD:
	case LS_SIGNAL_SLTM:
	case LS_SIGNAL_SLTA:
	case LS_SIGNAL_SSLTM:
	case LS_SIGNAL_SSLTA:
		lk_message_for_routing(lk, mp);
		return;
		/* 
		 *  CBA is sent as a normal message with normal load-sharing.  So,
		 *  send it to routing if routing is available.
		 */
	case LS_SIGNAL_CBA:
		ls_message_for_routing(ls, mp);
		return;
	}
	/* 
	 *  The remainder of the signalling network management messages are sent
	 *  on the first available link or sent up to routing.
	 */
	if (!(*g & LS_FLAGS_ADJ_INACCESSIBLE)) {
		int la = ls->statem.links_available;
		if (((*f & LK_FLAGS_UNAVAILABLE) && (la > 0)) || (la > 1)) {
			/* 
			 *  If we have other links available in the link set, send the
			 *  message on the first available link in this link set (but not
			 *  on the specified link).
			 */
			lk_t *lkp;
			for (lkp = ls->device; lkp; lkp = lkp->next) {
				unsigned long *h = &lkp->statem.flags;
				if (!(*h & LK_FLAGS_UNAVAILABLE) && lkp->statem.slc != slc) {
					p->slm.mh.rl.sls = lkp->statem.slc;	/* should be link
										   actually sent on 
										 */
					lk_message_for_routing(lkp, mp);
					return;
				}
			}
		}
	}
	/* give up */
	freemsg(mp);
}

static inline void lk_send_coo(lk_t * lk, mblk_t * mp)
{
	if ((mp = reallocb(mp, M_PROTO, LS_SIGNAL_COO_SIZE, BPRI_HI))) {
		((ls_prim_t *) mp->b_rptr)->coo.fsnc = lk->statem.fsnc;
		lk_send(lk, mp, LS_SIGNAL_COO);
	}
}

static inline void lk_send_coa(lk_t * lk, mblk_t * mp)
{
	if ((mp = reallocb(mp, M_PROTO, LS_SIGNAL_COA_SIZE, BPRI_HI))) {
		((ls_prim_t *) mp->b_rptr)->coa.fsnc = lk->statem.fsnc;
		lk_send(lk, mp, LS_SIGNAL_COA);
	}
}

static inline void lk_send_cbd(lk_t * lk, mblk_t * mp, int cbc)
{
	if ((mp = reallocb(mp, M_PROTO, LS_SIGNAL_CBD_SIZE, BPRI_HI))) {
		((ls_prim_t *) mp->b_rptr)->cbd.cbc = cbc;
		lk_send(lk, mp, LS_SIGNAL_CBD);
	}
}

static inline void lk_send_cba(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, mp, LS_SIGNAL_CBA);
}
static inline void lk_send_eco(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_ECO_SIZE, BPRI_HI), LS_SIGNAL_ECO);
}
static inline void lk_send_eca(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_ECA_SIZE, BPRI_HI), LS_SIGNAL_ECA);
}
static inline void lk_send_lin(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LIN_SIZE, BPRI_HI), LS_SIGNAL_LIN);
}
static inline void lk_send_lia(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LIA_SIZE, BPRI_HI), LS_SIGNAL_LIA);
}
static inline void lk_send_lid(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LID_SIZE, BPRI_HI), LS_SIGNAL_LID);
}
static inline void lk_send_lun(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LUN_SIZE, BPRI_HI), LS_SIGNAL_LUN);
}
static inline void lk_send_lua(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LUA_SIZE, BPRI_HI), LS_SIGNAL_LUA);
}
static inline void lk_send_lfu(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LFU_SIZE, BPRI_HI), LS_SIGNAL_LFU);
}
static inline void lk_send_lli(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LLI_SIZE, BPRI_HI), LS_SIGNAL_LLI);
}
static inline void lk_send_lri(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, reallocb(mp, M_PROTO, LS_SIGNAL_LRI_SIZE, BPRI_HI), LS_SIGNAL_LRI);
}

static inline void lk_send_sltm(lk_t * lk, mblk_t * mp)
{
	/* FIXME: adjust size to include test pattern */
	if ((mp = reallocb(mp, M_PROTO, LS_SIGNAL_SLTM_SIZE, BPRI_HI)))
		/* FIXME: fill out test length indicator and test pattern */
		lk_send(lk, mp, LS_SIGNAL_SLTM);
}

static inline void lk_send_ssltm(lk_t * lk, mblk_t * mp)
{
	/* FIXME: adjust size to include test pattern */
	if ((mp = reallocb(mp, M_PROTO, LS_SIGNAL_SSLTM_SIZE, BPRI_HI)))
		/* FIXME: fill out test length indicator and test pattern */
		lk_send(lk, mp, LS_SIGNAL_SSLTM);
}

static inline void lk_send_sslta(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, mp, LS_SIGNAL_SSLTA);
}
static inline void lk_send_slta(lk_t * lk, mblk_t * mp)
{
	lk_send(lk, mp, LS_SIGNAL_SLTA);
}

/*
 * ==========================================================================
 *
 *  Procedures
 *
 * ==========================================================================
 */

/*
 * --------------------------------------------------------------------------
 *
 *  Message handling
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_message_for_discrimination(lk_t * lk, mblk_t * mp)
{
	ls_t *ls = lk->module;
	ls_prim_t *p = (ls_prim_t *) mp->b_rptr;

	if ((p->sig.mh.ni == ls->config.ni) &&
	    (p->sig.mh.rl.opc == ls->config.apc) && (p->sig.mh.rl.dpc == ls->config.lpc)) {
		switch (p->sig.signal & LS_SIGNALS_MASK_H0) {
		case LS_SIGNAL_CHM:
		case LS_SIGNAL_ECM:
		case LS_SIGNAL_MIM:
		case LS_SIGNAL_DLM:
			ls_message_for_distribution(ls, mp);
			return;
		default:
			break;
		}
	}
	ls->ucalls->ls_msg_for_discrim(ls, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Signalling Link Test Control; (see Q.707)
 *
 *  -------------------------------------------------------------------------
 */

static inline void lk_slt_failed(lk_t * lk);

static void lk_tt1_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_SLTM_SENT)
		lk_slt_failed(lk);
}

static void lk_tt2_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_INACTIVE)) {
		*f |= LK_FLAGS_SLTM_SENT;
		lk_send_sltm(lk, NULL);
		lk_timer_start(tt1);
		lk_timer_start(tt2);
	}
}

static inline void lk_slt_failed(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_2ND_SLT) {
		*f &= ~LK_FLAGS_2ND_SLT;
		if (*f & LK_FLAGS_1ST_SLT) {
			*f &= ~LK_FLAGS_1ST_SLT;
			*f &= ~LK_FLAGS_SLTM_SENT;
			lk_timer_stop(tt1);
			lk_timer_stop(tt2);
			lk_failed(lk);
		}
	} else {
		*f |= LK_FLAGS_2ND_SLT;
		lk_timer_stop(tt1);
		*f |= LK_FLAGS_SLTM_SENT;
		lk_sltm(lk, NULL);
		lk_timer_start(tt1);
	}
}

static inline void lk_start_slt(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	*f |= LK_FLAGS_1ST_SLT;
	*f &= ~LK_FLAGS_2ND_SLT;
	*f |= LK_FLAGS_SLTM_SENT;
	lk_send_sltm(lk, NULL);
	lk_timer_start(tt1);
	lk_timer_start(tt2);
}

static inline void lk_stop_slt(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	*f &= ~LK_FLAGS_1ST_SLT;
	*f &= ~LK_FLAGS_2ND_SLT;
	*f &= ~LK_FLAGS_SLTM_SENT;
	lk_timer_stop(tt1);
	lk_timer_stop(tt2);
}

static void lk_sltm(lk_t * lk, mblk_t * mp)
{
	lk_send_slta(lk, mp);
}

static void lk_slta(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_SLTM_SENT) {
		*f &= ~LK_FLAGS_SLTM_SENT;
		lk_timer_stop(tt1);
		/* 
		 *  FIXME: need to recall and compare pattern and check SLC.
		 */
		if (1 /* compare test pattern */ ) {
			if (*f & LK_FLAGS_1ST_SLT) {
				*f &= ~LK_FLAGS_1ST_SLT;
				if (*f & LK_FLAGS_RESTORE_PENDING) {
					*f &= ~LK_FLAGS_RESTORE_PENDING;
					lk_restored(lk);
				} else
					lk_activated(lk);
			}
		} else
			lk_slt_failed(lk);
	}
	freemsg(mp);
}

static void lk_ssltm(lk_t * lk, mblk_t * mp)
{
	lk_send_sslta(lk, mp);
}

static void lk_sslta(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_SSLTM_SENT) {
		/* FIXME: need to recall and compare pattern. */
	}
	freemsg(mp);
}

/*
 * --------------------------------------------------------------------------
 *
 *  Link Status changes: (see Q.704 clause 3.2 and 3.3)
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_failed(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *g = &ls->statem.flags;
	unsigned long *f = &lk->statem.flags;

	lk_stop_slt(lk);
	if (!(*f & LK_FLAGS_INACTIVE)) {
		/* failed while active */
		*f |= LK_FLAGS_FAILED;
		*f |= LK_FLAGS_RESTORE_PENDING;
		if (!(*f & (LK_FLAGS_DEACTIVATED | LK_FLAGS_BLOCKED | LK_FLAGS_INHIBITED)))
			lk_unavailable(lk);
		else {
			if ((*f & LK_FLAGS_TIME_CTRL_CO) && !(*g & LS_FLAGS_ADJ_INACCESSIBLE)) {
				/* cancel time-controlled changeover */
				*f &= ~LK_FLAGS_TIME_CTRL_CO;
				lk_timer_stop(t1);
				lk_timer_start(t2);
				lk_sl_retrieve_bsnt(lk);
			}
		}
	} else
		/* failed while restoring or activating */
		lk_timer_start(t17);
}

static inline void lk_restored(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_INACTIVE) {
		lk_timer_stop(t17);
		*f &= ~(LK_FLAGS_INACTIVE | LK_FLAGS_RESTORE_PENDING);
		if (!(*f & LK_FLAGS_UNAVAILABLE))
			lk_available(lk);
	}
}

static inline void lk_deactivated(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *g = &ls->statem.flags;
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_INACTIVE)) {
		lk_timer_stop(t17);
		*f |= LK_FLAGS_DEACTIVATED;
		*f &= ~LK_FLAGS_RESTORE_PENDING;
		if (!(*f & (LK_FLAGS_FAILED | LK_FLAGS_BLOCKED | LK_FLAGS_INHIBITED)))
			lk_unavailable(lk);
		else {
			if ((*f & LK_FLAGS_TIME_CTRL_CO) && !(*g & LS_FLAGS_ADJ_INACCESSIBLE)) {
				/* cancel time-controlled changeover */
				*f &= ~LK_FLAGS_TIME_CTRL_CO;
				lk_timer_stop(t1);
				lk_timer_start(t2);
				lk_sl_retrieve_bsnt(lk);
			}
		}
	}
}

static inline void lk_activated(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_INACTIVE) {
		lk_timer_stop(t17);
		*f &= ~(LK_FLAGS_INACTIVE | LK_FLAGS_RESTORE_PENDING);
		if (!(*f & LK_FLAGS_UNAVAILABLE))
			lk_available(lk);
	}
}

static inline void lk_linkset_blocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_LINKSET_BLOCKED)) {
		if (!(*f & LK_FLAGS_LOCAL_BLOCKED))
			lk_sl_local_processor_outage(lk);
		*f |= LK_FLAGS_LINKSET_BLOCKED;

		if (!(*f & LK_FLAGS_UNAVAILABLE & ~LK_FLAGS_LINKSET_BLOCKED))
			lk_unavailable(lk);
	}
}

static inline void lk_linkset_unblocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LINKSET_BLOCKED) {
		*f &= ~LK_FLAGS_LINKSET_BLOCKED;
		if (!(*f & LK_FLAGS_BLOCKED)) {
			if (*f & LK_FLAGS_CHANGEOVER)
				lk_sl_resume(lk);
			else
				lk_sl_clear_buffers(lk);
			if (!(*f & LK_FLAGS_UNAVAILABLE))
				lk_available(lk);
		}
	}
}

static inline void lk_local_blocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_LINK_BLOCKED)) {
		if (!(*f & LK_FLAGS_LOCAL_BLOCKED))
			lk_sl_local_processor_outage(lk);
		*f |= LK_FLAGS_LINK_BLOCKED;
		if (!(*f & LK_FLAGS_UNAVAILABLE & ~LK_FLAGS_LINK_BLOCKED))
			lk_unavailable(lk);
	}
}

static inline void lk_local_unblocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LINK_BLOCKED) {
		*f &= ~LK_FLAGS_LINK_BLOCKED;
		if (!(*f & LK_FLAGS_BLOCKED)) {
			if (*f & LK_FLAGS_CHANGEOVER)
				lk_sl_resume(lk);
			else
				lk_sl_clear_buffers(lk);
			if (!(*f & LK_FLAGS_UNAVAILABLE))
				lk_available(lk);
		}
	}
}

static inline void lk_remote_blocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_REMOTE_BLOCKED)) {
		*f |= LK_FLAGS_REMOTE_BLOCKED;
		if (!(*f & LK_FLAGS_UNAVAILABLE & ~LK_FLAGS_REMOTE_BLOCKED))
			lk_unavailable(lk);
	}
}

static inline void lk_remote_unblocked(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_REMOTE_BLOCKED) {
		*f &= ~LK_FLAGS_REMOTE_BLOCKED;
		if (!(*f & LK_FLAGS_BLOCKED)) {
			if (*f & LK_FLAGS_CHANGEOVER)
				lk_sl_resume(lk);
			else
				lk_sl_clear_buffers(lk);
			if (!(*f & LK_FLAGS_UNAVAILABLE))
				lk_available(lk);
		}
	}
}

static void lk_local_inhibited(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_LOCAL_INHIBITED)) {
		if (!(*f & LK_FLAGS_INHIBITED))
			ls->ucalls->ls_inhibited(lk->module, lk->statem.slc);
		*f |= LK_FLAGS_LOCAL_INHIBITED;
		if (!(*f & LK_FLAGS_UNAVAILABLE & ~LK_FLAGS_LOCAL_INHIBITED))
			lk_unavailable(lk);
	}
}

static void lk_local_uninhibited(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LOCAL_INHIBITED) {
		lk_timer_stop(t22);
		*f &= ~LK_FLAGS_LOCAL_INHIBITED;
		if (!(*f & LK_FLAGS_INHIBITED))
			ls->ucalls->ls_uninhibited(lk->module, lk->statem.slc);
		if (!(*f & LK_FLAGS_UNAVAILABLE))
			lk_available(lk);
	}
}

static void lk_remote_inhibited(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_REMOTE_INHIBITED)) {
		if (!(*f & LK_FLAGS_INHIBITED))
			ls->ucalls->ls_inhibited(lk->module, lk->statem.slc);
		*f |= LK_FLAGS_REMOTE_INHIBITED;
		if (!(*f & LK_FLAGS_UNAVAILABLE & ~LK_FLAGS_REMOTE_INHIBITED))
			lk_unavailable(lk);
	}
}

static void lk_remote_uninhibited(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_REMOTE_INHIBITED) {
		lk_timer_stop(t23);
		*f &= ~LK_FLAGS_REMOTE_INHIBITED;
		if (!(*f & LK_FLAGS_INHIBITED))
			ls->ucalls->ls_uninhibited(lk->module, lk->statem.slc);
		if (!(*f & LK_FLAGS_UNAVAILABLE))
			lk_available(lk);
	}
}

/*
 * --------------------------------------------------------------------------
 *
 *  Link arrangements:
 *
 * --------------------------------------------------------------------------
 */

/*
 *  Link arrangements:
 *
 *  16:  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1 min  1 = 16/9
 *  15:  2  1  1  1  1  1  1  1  1  1  1  1  1  1  1  - min  1 = 16/9
 *  14:  2  2  1  1  1  1  1  1  1  1  1  1  1  1  -  - min  1 = 16/9
 *  13:  2  2  2  1  1  1  1  1  1  1  1  1  1  -  -  - min  1 = 16/9
 *  12:  2  2  2  2  1  1  1  1  1  1  1  1  -  -  -  - min  1 = 16/9
 *  11:  2  2  2  2  2  1  1  1  1  1  1  -  -  -  -  - min  1 = 16/9
 *  10:  2  2  2  2  2  2  1  1  1  1  -  -  -  -  -  - min  1 = 16/9
 *   9:  2  2  2  2  2  2  2  1  1  -  -  -  -  -  -  - min  1 = 16/9
 *   8:  2  2  2  2  2  2  2  2  -  -  -  -  -  -  -  - min  2 = 16/8
 *   7:  3  3  2  2  2  2  2  -  -  -  -  -  -  -  -  - min  2 = 16/7
 *   6:  3  3  3  3  2  2  -  -  -  -  -  -  -  -  -  - min  2 = 16/6
 *   5:  4  3  3  3  3  -  -  -  -  -  -  -  -  -  -  - min  3 = 16/5
 *   4:  4  4  4  4  -  -  -  -  -  -  -  -  -  -  -  - min  4 = 16/4
 *   3:  6  5  5  -  -  -  -  -  -  -  -  -  -  -  -  - min  5 = 16/3
 *   2:  8  8  -  -  -  -  -  -  -  -  -  -  -  -  -  - min  8 = 16/2
 *   1: 16  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - min 16 = 16/1
 *   0;  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
 *
 *   Rules:  take m = 16/n where n is the number of links that we are
 *   moving to (changeover or changeback).
 *
 *   On changeover, for each alternative physical link, add logical
 *   links to the physical link until the number of logical links for
 *   the physical link reaches (m-1).  Then, again, for each physical
 *   link, add one logical link until we run out of remaining logical
 *   links.
 *
 *   On changeback, for each alternative physical link, remove logical
 *   links until the number of logical links assigned to the current
 *   alternative link is (m), or until (m) links have been removed.
 */

/*
 *  Datastructures for link sets, logical channels and
 *  links:
 *
 *           ^ q
 *           |
 *     +-----+----+        ls->llinks.link
 *     |          +-----+-----+-----+-----+ - - +-----+
 *     |    ls    |  0  |  1  |  2  |  3  |     | 15  |
 *     |          +--+--+--+--+--+--+--+--+ -+- +--+--+
 *     +-----+----+  |     |     |     |           |
 *       ^   |        \   /       \   /           /
 *       | ls->device  \  |        \ /           |
 *       |   |          | |         X            |
 *       |   |          | |        / |  c is lk->statem.channels
 *       |   |          | | +-----/  |           |
 *       |   |          | | |        |           |
 *       |   |          v v v        v           v
 *       |   |      +--------+   +--------+   +--------+
 *       |   +----->| c = 3  +-->| c = 1  +-->|  c = 1 +--> NULL
 *       |          |   lk   | n |   lk   | n |   lk   | n
 *       +----------+        | e |        | e |        | e
 *       lk->module +----+---+ x +----+---+ x +----+---+ x
 *                       |     t      |     t      |     t
 *                       v q          v q          v q
 */

/*
 * --------------------------------------------------------------------------
 *
 *  Changeover procedures: (see Q.704 clause 5)
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_update_buffer(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_BLOCKED) {
		if (!(*f & LK_FLAGS_LOCAL_BLOCKED))
			lk_sl_clear_buffers(lk);
		lk_changeover_complete(lk);
	} else {
		*f |= LK_FLAGS_UPDATING;
		if (*f & LK_FLAGS_FSNC_PROVIDED) {
			/* get RTB + TB */
			lk_sl_retrieval_request_and_fsnc(lk, lk->statem.fsnc);
		} else {
			/* get TB only */
			lk_sl_clear_rtb(lk);
			lk_sl_retrieval_request_and_fsnc(lk, 0);
		}
	}
}

static void lk_t1_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_TIME_CTRL_CO)
		lk_update_buffer(lk);
}

static void lk_t2_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_CO_SENT)
		lk_update_buffer(lk);
}

static void lk_t17_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_INACTIVE)
		lk_sl_start(lk);
}

static inline void lk_changeover_complete(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;
	unsigned long *g = &ls->statem.flags;
	int ml = (((0x1 << ls->option.pvar) & LS_SLS_TYPES) ? 16 : 8)
	    * (ls->config.type & LS_TYPE_DOUBLE) ? 2 : 1;

	if (*g & LS_FLAGS_AVAILABLE) {
		if (ls->statem.links_loaded) {
			/* 
			 *  If the link set is still available, then redirect the link
			 *  streams to the appropriate links and empty the changeover
			 *  buffers into those links.
			 */
			lk_t *la;
			ll_t *ll;
			mblk_t *mp;
			int i = 0;
			int m = ml / ls->statem.links_loaded;
			for (la = ls->device; lk->statem.c > 0; lk->statem.c--) {
				for (; la && (la->statem.c >= m || la->statem.c == 0);
				     la = la->next);
				if (!la)
					break;
				for (; i < ml && ls->llinks[i].link != lk; i++);
				if (i >= ml)
					break;
				ll = &ls->llinks[i];
				ll->link = la;
				ll->flags &= ~(LL_FLAGS_STOPPED | LL_FLAGS_CHANGEOVER);
				while ((mp = bufq_dequeue(&ll->rtr_buf)))
					lk_message_for_routing(la, mp);
				while ((mp = bufq_dequeue(&ll->cob_buf)))
					lk_message_for_routing(la, mp);
				la->statem.c++;
			}
			for (la = ls->device; lk->statem.c > 0; lk->statem.c--) {
				for (; la && (la->statem.c > m || la->statem.c == 0);
				     la = la->next);
				if (!la)
					break;
				for (; i < ml && ls->llinks[i].link != lk; i++);
				if (i >= ml)
					break;
				ll = &ls->llinks[i];
				ll->link = la;
				ll->flags &= ~(LL_FLAGS_STOPPED | LL_FLAGS_CHANGEOVER);
				while ((mp = bufq_dequeue(&ll->rtr_buf)))
					lk_message_for_routing(la, mp);
				while ((mp = bufq_dequeue(&ll->cob_buf)))
					lk_message_for_routing(la, mp);
				la->statem.c++;
			}
		} else {
		}
	} else {
		/* 
		 *  If the link set is not available, then there is nowhere to
		 *  redirect link streams.  Transfer changeover buffers up to routing
		 *  and send routing a retrieval complete.
		 */
		ls->ucalls->ls_retrieval_complete(ls);
	}
	*f &= ~LK_FLAGS_TRAFFIC;	/* no more traffic on link */
	ls->statem.links_loaded--;
	*f &= ~LK_FLAGS_CO_FLAGS;
	if (*f & LK_FLAGS_RESTORE_PENDING) {
		*f &= ~LK_FLAGS_RESTORE_PENDING;
		if (*f & LK_FLAGS_INACTIVE)
			lk_sl_start(lk);
	}
}

static inline void lk_unavailable(lk_t * lk)
{
	ls_t *ls = lk->module;
	int slc = lk->statem.slc;
	unsigned long *f = &lk->statem.flags;
	unsigned long *g = &ls->statem.flags;
	int ml = (((0x1 << ls->option.pvar) & LS_SLS_TYPES) ? 16 : 8)
	    * (ls->config.type & LS_TYPE_DOUBLE) ? 2 : 1;

	/* stop traffic */
	if ((--ls->statem.links_available)) {
		int i;
		for (i = 0; i < ml; i++)
			if (ls->llinks[i].link->statem.slc == slc)
				ls->llinks[i].flags |= (LL_FLAGS_STOPPED | LL_FLAGS_CHANGEOVER);
	} else {
		*g &= ~LS_FLAGS_AVAILABLE;
		ls->ucalls->ls_unavailable(ls);
	}
	if (!(*g & LS_FLAGS_TRAFFIC) || !(*f & LK_FLAGS_TRAFFIC)) {
		/* divert immediately if no traffic on linkset or link */
		lk_changeover_complete(lk);
	} else {
		*f |= LK_FLAGS_CHANGEOVER;
		if (!(*g & LS_FLAGS_ADJ_INACCESSIBLE) && (*f & LK_FLAGS_INACTIVE)) {
			/* buffer update procedure */
			lk_timer_start(t2);
			lk_sl_retrieve_bsnt(lk);
		} else {
			/* time-controlled changeover */
			*f |= LK_FLAGS_TIME_CTRL_CO;
			lk_timer_start(t1);
		}
	}
}

static inline void lk_coo(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	*f |= LK_FLAGS_CO_RCVD;
	*f |= LK_FLAGS_FSNC_PROVIDED;
	lk->statem.fsnc = ((ls_prim_t *) mp->b_rptr)->coo.fsnc;
	if (*f & LK_FLAGS_CHANGEOVER) {
		if ((*f & LK_FLAGS_BSNT_RETRIEVED))
			lk_send_coa(lk, mp);
		else
			lk_send_eca(lk, mp);
	} else {
		if (!(*f & LK_FLAGS_INACTIVE)) {
			lk_sl_stop(lk);
			lk_failed(lk);
		}
		freemsg(mp);
	}
	lk_update_buffer(lk);
}

static inline void lk_coa(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_CO_SENT) {
		lk_timer_stop(t2);
		*f |= LK_FLAGS_FSNC_PROVIDED;
		lk->statem.fsnc = ((ls_prim_t *) mp->b_rptr)->coa.fsnc;
		lk_update_buffer(lk);
	}
	freemsg(mp);
}

static inline void lk_eco(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	*f |= LK_FLAGS_CO_RCVD;
	if (*f & LK_FLAGS_CHANGEOVER) {
		if ((*f & LK_FLAGS_CO_SENT) && (*f & LK_FLAGS_BSNT_RETRIEVED))
			lk_send_coa(lk, mp);
		else
			lk_send_eca(lk, mp);
	} else {
		if (!(*f & LK_FLAGS_INACTIVE)) {
			lk_sl_stop(lk);
			lk_failed(lk);
		}
		freemsg(mp);
	}
	lk_update_buffer(lk);
}

static inline void lk_eca(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_CO_SENT) {
		lk_timer_stop(t2);
		lk_update_buffer(lk);
	}
	freemsg(mp);
}

/*
 * --------------------------------------------------------------------------
 *
 *  Changeback procedures: (see Q.704 clause 6)
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_deactivate(lk_t * lk);

static void lk_available(lk_t * lk)
{
	ls_t *ls = lk->module;
	int slc = lk->statem.slc;
	int sli = lk->statem.sli;
	unsigned long *f = &lk->statem.flags;
	unsigned long *g = &ls->statem.flags;
	int ml = (((0x1 << ls->option.pvar) & LS_SLS_TYPES) ? 16 : 8)
	    * (ls->config.type & LS_TYPE_DOUBLE) ? 2 : 1;

	if (*f & LK_FLAGS_CHANGEOVER) {
		int i;
		lk_timer_stop(t1);
		lk_timer_stop(t2);
		for (i = 0; i < ml; i++)
			if (ls->llinks[i].link->statem.slc == slc)
				ls->llinks[i].flags &= ~LL_FLAGS_CHANGEOVER;
		if (!(*f & LK_FLAGS_UPDATING))
			lk_changeover_complete(lk);
	} else {
		if (++(ls->statem.links_available) > ls->config.links_specified) {
			lk_deactivate(lk);
		} else {
			int i, m, k[LS_MAX_LINKS] = { 0, };
			/* 
			 *   On changeback, for each alternative physical link (i), remove
			 *   logical links until the number of logical links assigned to
			 *   the current alternative link is (m), or until the required
			 *   number of links (k[sli] == m) have been removed.
			 *
			 *   To ensure that SLS values align with SLC values (that is, so
			 *   that an SLS value which identifies a link set always
			 *   identifies a logical stream associated with that link set, it
			 *   is necessary to move the one stream with SLS == SLC back
			 *   first on a changeover.
			 */
			m = ml / ++(ls->statem.links_loaded);

			ls->llinks[sli].link = lk;
			ls->llinks[sli].flags |= (LL_FLAGS_STOPPED || LL_FLAGS_CHANGEBACK);
			k[sli] = 1;

			for (i = 0; i < ml && k[sli] < m; i++) {
				if (i == sli || m == ml || k[ls->llinks[i].link->statem.sli] == m) {
					ls->llinks[i].link = lk;
					ls->llinks[i].flags |= LL_FLAGS_STOPPED;
					ls->llinks[i].flags |= LL_FLAGS_CHANGEBACK;
					k[sli]++;
				} else
					k[ls->llinks[i].link->statem.sli]++;
			}
			if (!(*g & LS_FLAGS_AVAILABLE)) {
				*g |= LS_FLAGS_AVAILABLE;
				ls->ucalls->ls_available(ls);
			}
		}
	}
}

/*
 * --------------------------------------------------------------------------
 *
 *  Management Inhibiting procedures: (see Q.704 clause 10)
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_inhibit(lk_t * lk, mblk_t * mp);
static inline void lk_uninhibit(lk_t * lk, mblk_t * mp);
static inline void lk_force_uninhibit(lk_t * lk, mblk_t * mp);

static void lk_t12_timeout(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *g = &ls->statem.flags;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LUN_SENT) {
		if (*f & LK_FLAGS_T12_EXPIRED || (*g & LS_FLAGS_ADJ_INACCESSIBLE)) {
			ls->ucalls->ls_uninhibit_failed(lk->module, lk->statem.slc);
			*f &= ~LK_FLAGS_T12_EXPIRED;
			*f &= ~LK_FLAGS_LUN_SENT;
			if (*f & LK_FLAGS_LIN_PENDING) {
				*f &= ~LK_FLAGS_LIN_PENDING;
				lk_inhibit(lk, NULL);
			}
		} else {
			*f &= ~LK_FLAGS_LUN_SENT;
			*f |= LK_FLAGS_T12_EXPIRED;
			lk_uninhibit(lk, NULL);
		}
	}
}

static void lk_t13_timeout(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LFU_SENT) {
		if ((*f & (LK_FLAGS_T13_EXPIRED | LK_FLAGS_INACTIVE | LK_FLAGS_BLOCKED))) {
			/* make some decisions here before transferring up */
			ls->ucalls->ls_force_uninhibit_failed(lk->module);
			*f &= ~LK_FLAGS_T13_EXPIRED;
			*f &= ~LK_FLAGS_LFU_SENT;
		} else {
			*f &= ~LK_FLAGS_LFU_SENT;
			*f |= LK_FLAGS_T13_EXPIRED;
			lk_force_uninhibit(lk, NULL);
		}
	}
}

static void lk_t14_timeout(lk_t * lk)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;
	unsigned long *g = &ls->statem.flags;

	if (*f & LK_FLAGS_LIN_SENT) {
		/* FIXME: set this adjacent flag somewhere... */
		if ((*f & LK_FLAGS_T14_EXPIRED) || (*g & LS_FLAGS_ADJ_INACCESSIBLE)) {
			ls->ucalls->ls_inhibit_failed(lk->module, lk->statem.slc);
			*f &= ~LK_FLAGS_T14_EXPIRED;
			*f &= ~LK_FLAGS_LIN_SENT;
			if (*f & LK_FLAGS_LUN_PENDING) {
				*f &= ~LK_FLAGS_LUN_PENDING;
				lk_uninhibit(lk, NULL);
			}
		} else {
			*f &= ~LK_FLAGS_LIN_SENT;
			*f |= LK_FLAGS_T14_EXPIRED;
			lk_inhibit(lk, NULL);
		}
	}
}

static void lk_t22_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LOCAL_INHIBITED) {
		lk_send_lli(lk, NULL);
		lk_timer_start(t22);
	}
}

static void lk_t23_timeout(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_REMOTE_INHIBITED) {
		lk_send_lri(lk, NULL);
		lk_timer_start(t23);
	}
}

static inline void lk_force_uninhibit(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LFU_SENT) {
		if (mp)
			freemsg(mp);
	} else {
		lk_send_lfu(lk, mp);
		lk_timer_start(t13);
		*f |= LK_FLAGS_LFU_SENT;
	}
}

static inline void lk_inhibit(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LUN_PENDING)
		*f &= ~LK_FLAGS_LUN_PENDING;
	else {
		if (!(*f & (LK_FLAGS_LIN_SENT | LK_FLAGS_LIN_PENDING))) {
			if (*f & LK_FLAGS_LUN_SENT)
				*f |= LK_FLAGS_LIN_PENDING;
			else {
				lk_send_lin(lk, mp);
				lk_timer_start(t14);
				*f |= LK_FLAGS_LIN_SENT;
				return;
			}
		}
	}
	if (mp)
		freemsg(mp);
}

static inline void lk_uninhibit(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LIN_PENDING)
		*f &= ~LK_FLAGS_LIN_PENDING;
	else {
		if (!(*f & (LK_FLAGS_LUN_SENT | LK_FLAGS_LUN_PENDING))) {
			if (*f & LK_FLAGS_LIN_SENT)
				*f |= LK_FLAGS_LUN_PENDING;
			else {
				lk_send_lun(lk, mp);
				lk_timer_start(t12);
				*f |= LK_FLAGS_LUN_SENT;
				return;
			}
		}
	}
	if (mp)
		freemsg(mp);
}

static inline void lk_lin(lk_t * lk, mblk_t * mp)
{
	ls_t *ls = lk->module;
	unsigned long *g = &ls->statem.flags;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_REMOTE_INHIBITED)
		lk_send_lia(lk, mp);
	else {
		if (!(*f & LK_FLAGS_UNAVAILABLE) && (*g & LS_FLAGS_CRITICAL)
		    && (ls->statem.links_available < 2))
			lk_send_lid(lk, mp);
		else {
			lk_send_lia(lk, mp);
			lk_timer_start(t23);
			lk_remote_inhibited(lk);
		}
	}
}

static inline void lk_lun(lk_t * lk, mblk_t * mp)
{
	lk_send_lua(lk, mp);
	lk_remote_uninhibited(lk);
}

static inline void lk_lia(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LIN_SENT) {
		lk_timer_stop(t22);
		lk_local_inhibited(lk);
		if (*f & LK_FLAGS_LUN_PENDING) {
			*f &= ~LK_FLAGS_LUN_PENDING;
			lk_uninhibit(lk, mp);
			return;
		}
	}
	freemsg(mp);
}

static inline void lk_lua(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LUN_SENT) {
		lk_local_uninhibited(lk);
		if (*f & LK_FLAGS_LIN_PENDING) {
			*f &= ~LK_FLAGS_LIN_PENDING;
			lk_inhibit(lk, mp);
			return;
		}
	}
	freemsg(mp);
}

static inline void lk_lid(lk_t * lk, mblk_t * mp)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LUN_SENT) {
		lk_timer_stop(t14);
		*f &= ~LK_FLAGS_T14_EXPIRED;
		*f &= ~LK_FLAGS_LUN_SENT;
		ls->ucalls->ls_inhibit_denied(lk->module, lk->statem.slc);
	}
	freemsg(mp);
}

static inline void lk_lfu(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_LOCAL_INHIBITED))
		lk_send_lun(lk, mp);
	else
		lk_uninhibit(lk, mp);
}

static inline void lk_lli(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_REMOTE_INHIBITED)
		freemsg(mp);
	else
		lk_force_uninhibit(lk, mp);
}

static inline void lk_lri(lk_t * lk, mblk_t * mp)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_LOCAL_INHIBITED)
		freemsg(mp);
	else
		lk_uninhibit(lk, mp);
}

/*
 * --------------------------------------------------------------------------
 *
 *  Signalling Link Management: (see Q.704 clause 12)
 *
 * --------------------------------------------------------------------------
 */

static inline void lk_activate(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (*f & LK_FLAGS_INACTIVE)
		lk_sl_start(lk);
}

static inline void lk_deactivate(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;

	if (!(*f & LK_FLAGS_INACTIVE)) {
		lk_sl_stop(lk);
		lk_deactivated(lk);
	}
}

static inline void lk_dlc(lk_t * lk, mblk_t * mp)
{
	(void) lk;
	freemsg(mp);
	/* FIXME: make me dooo something... */
}

static inline void lk_css(lk_t * lk, mblk_t * mp)
{
	(void) lk;
	freemsg(mp);
	/* FIXME: make me dooo something... */
}

static inline void lk_cns(lk_t * lk, mblk_t * mp)
{
	(void) lk;
	freemsg(mp);
	/* FIXME: make me dooo something... */
}

static inline void lk_cnp(lk_t * lk, mblk_t * mp)
{
	(void) lk;
	freemsg(mp);
	/* FIXME: make me dooo something... */
}

/*
 * --------------------------------------------------------------------------
 *
 *  UPSTREAM Message Handling:
 *
 * --------------------------------------------------------------------------
 */

static inline void ls_message_for_discrimination(lk_t * lk, mblk_t * mp)
{
	(void) lk;
	freemsg(mp);
	/* FIXME: make me doooo something... */
}

/*
 * --------------------------------------------------------------------------
 *
 *  LK<-SL UPSTREAM Primitives
 *
 * --------------------------------------------------------------------------
 */

static void sl_pdu(lk_t * lk, mblk_t * mp)
{
	/* 
	 *  SL_PDU_IND is sent from L2 to L3 to indicate that a message has been
	 *  received on the signalling link.  Messages are always accepted on the
	 *  signalling link.
	 */
	if (lk_parse_msu(lk, mp)) {
		/* report this to logs somehow that we got a decode error */
		freemsg(mp);
		return;
	}
	ls_message_for_discrimination(lk, mp);
}

static inline void sl_link_congested(lk_t * lk, int cong, int disc)
{
	ls_t *ls = lk->module;
	/* 
	 * SL_LINK_CONGESTED_IND is sent from L2 to L3 to indicate that the
	 * signalling link has become congested.
	 */
	lk->statem.cong_status = cong;
	lk->statem.disc_status = disc;
	if (ls->statem.cong_status < lk->statem.cong_status ||
	    ls->statem.disc_status < lk->statem.disc_status) {
		if (ls->statem.cong_status < lk->statem.cong_status)
			ls->statem.cong_status = lk->statem.cong_status;
		if (ls->statem.disc_status < lk->statem.disc_status)
			ls->statem.disc_status = lk->statem.disc_status;
		ls->ucalls->ls_congested(ls, ls->statem.cong_status, ls->statem.disc_status);
	}
}

static inline void sl_link_congestion_ceased(lk_t * lk, int cong, int disc)
{
	ls_t *ls = lk->module;
	int new_cong = 0;
	int new_disc = 0;
	lk_t *l;
	/* 
	 *  SL_LINK_CONGESTION_CEASED_IND is sent from L2 to L3 to indicate that
	 *  the signalling link has become less congested.
	 */
	lk->statem.cong_status = cong;
	lk->statem.disc_status = disc;

	for (l = ls->device; l; l = l->next) {
		if (new_cong < l->statem.cong_status)
			new_cong = l->statem.cong_status;
		if (new_disc < l->statem.disc_status)
			new_disc = l->statem.disc_status;
	}
	if (new_cong < ls->statem.cong_status || new_disc < ls->statem.disc_status) {
		ls->ucalls->ls_congestion_ceased(ls, new_cong, new_disc);
	}
	ls->statem.cong_status = new_cong;
	ls->statem.disc_status = new_disc;
}

static inline void sl_retrieved_message(lk_t * lk, mblk_t * mp)
{
	ls_t *ls = lk->module;
	unsigned long *f = &lk->statem.flags;
	/* 
	 *  SL_RETRIEVED_MESSAGE_IND is sent from L2 to L3 to provide
	 *  messages which were retrieved from the RTB and TB of a failed or
	 *  deactivated signalling link.
	 *
	 *  The message must be redecoded and sent to the link set for rerouting.
	 */
	if (*f & LK_FLAGS_INACTIVE) {
		if (lk_parse_msu(lk, mp)) {
			/* report this to logs somehow that we got a decode error */
			freemsg(mp);
			return;
		}
		ls->ucalls->ls_retrieved_message(lk->module, mp);
	} else
		freemsg(mp);
}

static inline void sl_retrieval_complete(lk_t * lk)
{
	/* 
	 *  SL_RETRIEVAL_COMPLETE_IND is sent from L2 to L3 to indicate that all
	 *  the messages from the RTB and TB of the failed signalling link have
	 *  been retrieved.
	 */
	lk_changeover_complete(lk);
}

static void sl_rb_cleared(lk_t * lk)
{
	/* 
	 *  SL_RB_CLEARED_IND is sent from L2 to L3 when the signalling link
	 *  received buffer is cleared.
	 */
	(void) lk;
}

static void sl_bsnt(lk_t * lk, int bsnt)
{
	unsigned long *f = &lk->statem.flags;
	/* 
	 *  SL_BSNT_IND is sent from L2 to L3 when L3 requests retrieval of the
	 *  BSNT and BSNT retrieval is successful.
	 */
	if (!(*f & LK_FLAGS_CHANGEOVER))
		return;		/* cancelled */
	lk->statem.bsnt = bsnt;
	*f |= LK_FLAGS_BSNT_RETRIEVED;
	if (*f & LK_FLAGS_CO_RCVD)
		lk_send_coa(lk, NULL);
	else {
		*f |= LK_FLAGS_CO_SENT;
		lk_send_coo(lk, NULL);
		lk_timer_start(t2);
	}
}

static void sl_in_service(lk_t * lk)
{
	/* 
	 *  SL_IN_SERVICE_IND is sent from L2 to L3 when the link has completed
	 *  initial alignment and is ready to send traffic.  The link must first
	 *  pass a signalling link test, however.
	 */
	lk->module->ucalls->ls_in_service_at_l2(lk->module);
	lk_start_slt(lk);
}

static void sl_out_of_service(lk_t * lk, int reason)
{
	/* 
	 *  SL_OUT_OF_SERVICE_IND is sent from L2 to L3 when the signalling link
	 *  fails.  The Q.703 SDLs never return this indication when the
	 *  signalling link is intentionally deactivated with the L3 Stop command.
	 */
	(void) reason;		/* FIXME: do something with the reason??? */
	lk_failed(lk);
}

static void sl_remote_processor_outage(lk_t * lk)
{
	/* 
	 *  SL_REMOTE_PROCESSOR_OUTAGE_IND is sent from L2 to L3 whenever the
	 *  signalling link is remotely blocked (i.e. receiving SIPO).
	 */
	lk_remote_blocked(lk);
}

static void sl_remote_processor_recovered(lk_t * lk)
{
	/* 
	 *  SL_REMOTE_PROCESSOR_RECOVERED_IND is sent from L2 to L3 whenever the
	 *  signalling link is no longer remotely blocked.
	 */
	lk_remote_unblocked(lk);
}

static void sl_rtb_cleared(lk_t * lk)
{
	/* 
	 *  SL_RTB_CLEARED_IND is sent from L2 to L3 when the signalling link
	 *  retransmission buffer is cleared.
	 */
	(void) lk;
}

static void sl_retrieval_not_possible(lk_t * lk)
{
	/* 
	 * SL_RETRIEVAL_NOT_POSSIBLE_IND is sent from L2 to L3 to indicate that
	 * message retrieval is not possible.
	 */
	lk_changeover_complete(lk);
}

static void sl_bsnt_not_retrievable(lk_t * lk)
{
	unsigned long *f = &lk->statem.flags;
	/* 
	 *  SL_BSNT_NOT_RETRIEVABLE_IND is sent from L2 to L3 in response to a
	 *  SL_RETRIEVE_BSNT_REQ when BSNT retrieval is not possible.
	 */
	if (!(*f & LK_FLAGS_CHANGEOVER))
		return;		/* cancelled */
	if (*f & LK_FLAGS_CO_RCVD)
		lk_send_eca(lk, NULL);
	else {
		*f |= LK_FLAGS_CO_SENT;
		lk_send_eco(lk, NULL);
		lk_timer_start(t2);
	}
}

/*
 * --------------------------------------------------------------------------
 *
 *  LK<-MGMT Primitives
 *
 * --------------------------------------------------------------------------
 */

static void lk_local_processor_outage(lk_t * lk)
{
	lk_local_blocked(lk);
}

static void lk_local_processor_recovered(lk_t * lk)
{
	lk_local_unblocked(lk);
}

static void lk_message_for_routing(lk_t * lk, mblk_t * mp)
{
	if (lk_build_msu(lk, mp)) {
		/* log encode failure */
		freemsg(mp);
		return;
	}
	lk_sl_pdu(lk, mp);
}

static void lk_message_for_distribution(lk_t * lk, mblk_t * mp)
{
	ls_prim_t *p;

	p = (ls_prim_t *) mp->b_rptr;
	switch (p->sig.signal & LS_SIGNALS_MASK_H0) {
	case LS_SIGNAL_CHM:
		switch (p->sig.signal) {
		case LS_SIGNAL_COO:
			lk_coo(lk, mp);
			return;
		case LS_SIGNAL_COA:
			lk_coa(lk, mp);
			return;
		}
		break;
	case LS_SIGNAL_ECM:
		switch (p->sig.signal) {
		case LS_SIGNAL_ECO:
			lk_eco(lk, mp);
			return;
		case LS_SIGNAL_ECA:
			lk_eca(lk, mp);
			return;
		}
		break;
	case LS_SIGNAL_MIM:
		switch (p->sig.signal)
		case LS_SIGNAL_LIN:
			lk_lin(lk, mp);
		return;
	case LS_SIGNAL_LUN:
		lk_lun(lk, mp);
		return;
	case LS_SIGNAL_LIA:
		lk_lia(lk, mp);
		return;
	case LS_SIGNAL_LUA:
		lk_lua(lk, mp);
		return;
	case LS_SIGNAL_LID:
		lk_lid(lk, mp);
		return;
	case LS_SIGNAL_LFU:
		lk_lfu(lk, mp);
		return;
	case LS_SIGNAL_LLI:
		lk_lli(lk, mp);
		return;
	case LS_SIGNAL_LRI:
		lk_lri(lk, mp);
		return;
		{
		}
		break;
	case LS_SIGNAL_DLM:
		switch (p->sig.signal) {
		case LS_SIGNAL_DLC:
			lk_dlc(lk, mp);
			return;
		case LS_SIGNAL_CSS:
			lk_css(lk, mp);
			return;
		case LS_SIGNAL_CNS:
			lk_cns(lk, mp);
			return;
		case LS_SIGNAL_CNP:
			lk_cnp(lk, mp);
			return;
		}
		break;
	}
	/* inform management */
	freemsg(mp);
}

/*
 * --------------------------------------------------------------------------
 *
 *  LS<-RT DOWNSTREAM Primitives
 *
 * --------------------------------------------------------------------------
 */

/*
 *  LS_MESSAGE_FROM_ROUTING_REQ:-  These are messages from routing which are
 *  for transmission on this link set.  This includes messages initiated by
 *  other state machines, user parts and transferred with the transfer
 *  function.
 *
 *  Each load-shared message should go to the link unless there is buffering
 *  in effect for the chosen link.  User messages and changeback
 *  acknowledgements load-share normally.  Use the SLS in the message for load
 *  sharing.
 *
 *  Changeback declarations, force uninihibit messages and signalling link
 *  test messages should have been sent directly on link to which they apply.
 *
 *  Link uninhibit and uninhibit acknowledgement messages should have been sent
 *  directly on the link to which they apply if the adjacent signalling point
 *  was inaccessible.
 *
 *  All other signalling link and signalling traffic management messages
 *  should be sent on any available link.
 */
static inline void ls_message_from_routing(ls_t * ls, mblk_t * mp)
{
	ls_prim_t *p = (ls_prim_t *) mp->b_rptr;
	int sli, sls = p->sig.mh.rl.sls;

#ifdef LS_DEBUG
	switch (p->sig.signal) {
	case LS_SIGNAL_CBD:
	case LS_SIGNAL_LFU:
	case LS_SIGANL_SLTM:
	case LS_SIGANL_SLTA:
	case LS_SIGANL_SSLTM:
	case LS_SIGANL_SSLTA:
	{
		lk_t lk;
		DDPRINT(0, ("%s [%s %d] link specific message routing error\n",
			    __FUNCTION__, __FILE__, __LINE__));
		lk_message_for_routing(ls->plinks[p->slm.sli], mp);
		return;
	}
	}
#endif
	switch (p->sig.signal & LS_SIGNALS_MASK_H0) {
	case LS_SIGNAL_USER:
		break;
	case LS_SIGNAL_CHM:
		if (p->sig.signal == LS_SIGNAL_CBA)
			break;
	case LS_SIGNAL_ECM:
	case LS_SIGNAL_DLM:
	case LS_SIGNAL_MIM:
		/* 
		 *  Select an available link not equal SLC.
		 *
		 *  There is no specified algorithm for selecting the available
		 *  link in the link set.  We, therefore, choose the first
		 *  available link.
		 */
		if (ls->statem.flags & LS_FLAGS_AVAILABLE) {
			lk_t *la;
			int slc = p->slm.slc;
			for (la = ls->device; la; la = la->next) {
				if (!(la->statem.flags & LK_FLAGS_UNAVAILABLE) &&
				    la->statem.slc != slc) {
					lk_message_for_routing(la, mp);
					return;
				}
			}
		}
		/* error - no available link */
		freemsg(mp);
		return;
	case LS_SIGNAL_FCM:
	case LS_SIGNAL_TFM:
	case LS_SIGNAL_RSM:
	case LS_SIGNAL_TRM:
	case LS_SIGNAL_UFC:
		break;
	}
	/* 
	 *  Normal load-sharing by SLS.
	 */
	if ((0x1 << ls->option.pvar) & LS_SLS_TYPES) {
		/* Use ANSI style SLS selection */
		if (ls->config.type & LS_TYPE_DOUBLE)
			sli = sls & 0x1f;
		else
			sli = (sls >> 1) & 0x1f;
		p->sig.mh.rl.sls = ((sls << 4) & 0x1) | ((sls >> 1) & 0xf);	/* rotate */
	} else {
		/* Use ITUT style SLS selection */
		if (ls->config.type & LS_TYPE_DOUBLE)
			sli = sls & 0xf;
		else {
			if (ls->config.type & LS_TYPE_STP)
				sli = (sls >> 1) & 0x7;
			else
				sli = ((sls & 0x1) | ((sls >> 1) & ~0x1)) & 0x7;
		}
	}
	if (ls->llinks[sli].flags & LL_FLAGS_STOPPED)
		bufq_queue(&ls->llinks[sli].cob_buf, mp);
	else
		lk_message_for_routing(ls->llinks[sli].link, mp);
}

/*
 *  This is a message (decoded) for rerouting from the route.  This should be
 *  buffered in a retrieval buffer or discarded.
 */
static inline void ls_message_for_rerouting(ls_t * ls, mblk_t * mp)
{
	ls_prim_t *p = (ls_prim_t *) mp->b_rptr;
	int sls = p->signals.sig.mh.rl.sls & 0xf;

	if (ls->llinks[sls].flags & LL_FLAGS_STOPPED)
		bufq_queue(&ls->llinks[sls].rtr_buf, mp);
	else
		freemsg(mp);	/* should probably complain here */
}

/*
 *  This is a message (decoded) for distribution.  These are MTP management
 *  messages only.  The decoded message contains information concerning which
 *  link the message corresponds to.  Messages received by the link set should
 *  have the OPC equal to the adjacent signalling point and DPC equal to the
 *  local signalling point.  The SLC field indicates the signalling link
 *  selection stream (and thus the link) that the message corresponds to.
 */
static inline void ls_message_for_distribution(ls_t * ls, mblk_t * mp)
{
	ls_prim_t *p = (ls_prim_t *) mp->b_rptr;

	switch (p->sig.signal) {
	case LS_SIGNAL_CBD:
		ls_cbd(ls, mp);
		return;
	case LS_SIGNAL_CBA:
		ls_cba(ls, mp);
		return;
	default:
	{
		lk_t *lk;
		/* FIXME: convert slc to sli properly.... */
		int sli = p->slm.slc;
		if ((lk = ls->plinks[sli])) {
			lk_message_for_distribution(lk, mp);
			return;
		}
		/* message received relating to non-existent link! */
	}
	}
	/* FIXME: syslog a software error. */
	freemsg(mp);
}

static inline void ls_link_activate(ls_t * ls, int slc)
{
	lk_t *lk;
	int sli = slc;			/* FIXME: convert slc to sli properly... */
	if ((lk = ls->plinks[sli])) {
		lk_activate(lk);
		return;
	}
	/* received activate for non-existent link! */
}

static inline void ls_link_deactivate(ls_t * ls, int slc)
{
	lk_t *lk;
	int sli = slc;			/* FIXME: convert slc to sli properly... */
	if ((lk = ls->plinks[sli])) {
		lk_deactivate(lk);
		return;
	}
	/* received deactivate for non-existent link! */
}

static inline void ls_link_inhibit(ls_t * ls, int slc)
{
	lk_t *lk;
	int sli = slc;			/* FIXME: convert slc to sli properly... */
	if ((lk = ls->plinks[sli])) {
		lk_inhibit(lk, NULL);
		return;
	}
	/* received link inhibit for non-existent link! */
}

static inline void ls_link_uninhibit(ls_t * ls, int slc)
{
	lk_t *lk;
	int sli = slc;			/* FIXME: convert slc to sli properly... */
	if ((lk = ls->plinks[sli])) {
		lk_uninhibit(lk, NULL);
		return;
	}
	/* received link uninhibit for non-existent link! */
}

static inline void ls_activate(ls_t * ls)
{
	lk_t *lk;
	for (lk = ls->device; lk; lk = lk->next)
		lk_activate(lk);
}

static inline void ls_deactivate(ls_t * ls)
{
	lk_t *lk;
	for (lk = ls->device; lk; lk = lk->next)
		lk_deactivate(lk);
}

static inline void ls_inhibit(ls_t * ls)
{
	lk_t *lk;
	for (lk = ls->device; lk; lk = lk->next)
		lk_inhibit(lk, NULL);
}

static inline void ls_uninhibit(ls_t * ls)
{
	lk_t *lk;
	for (lk = ls->device; lk; lk = lk->next)
		lk_uninhibit(lk, NULL);
}

static inline void ls_force_uninhibit(ls_t * ls)
{
	lk_t *lk;
	/* pick a link to uninhibit */
	for (lk = ls->device; lk; lk = lk->next) {
		if (lk->statem.flags & LK_FLAGS_INHIBITED) {
			lk_force_uninhibit(lk, NULL);
			return;
		}
	}
}

static inline void ls_routing_outage(ls_t * ls)
{
	lk_t *lk;
	ls->statem.flags |= LS_FLAGS_ROUTING_OUTAGE;
	for (lk = ls->device; lk; lk = lk->next)
		lk_linkset_blocked(lk);
}
static inline void ls_routing_recovered(ls_t * ls)
{
	lk_t *lk;
	ls->statem.flags &= ~LS_FLAGS_ROUTING_OUTAGE;
	for (lk = ls->device; lk; lk = lk->next)
		lk_linkset_unblocked(lk);
}

/*
 *  Routing marks the signalling link set ass critical whenever there exists
 *  route sets which have this link set as the only available route to the
 *  destination.  It is removed when this is no longer the case.  This flag is
 *  used by signalling link management to determine when to deny inhibiting of
 *  a link.
 */
static inline void ls_critical(ls_t * ls)
{
	ls->statem.flags |= LS_FLAGS_CRITICAL;
}
static inline void ls_noncritical(ls_t * ls)
{
	ls->statem.flags &= ~LS_FLAGS_CRITICAL;
}

/*
 *  Routing marks the signalling link set whenever the signalling link becomes
 *  unavailable in a critical state.
 */
static inline void ls_emergency(ls_t * ls)
{
	lk_t *lk;
	ls->statem.flags |= LS_FLAGS_EMERGENCY;
	for (lk = ls->device; lk; lk = lk->next)
		lk_sl_emergency(lk);
}
static inline void ls_emergency_ceases(ls_t * ls)
{
	lk_t *lk;
	ls->statem.flags &= ~LS_FLAGS_EMERGENCY;
	for (lk = ls->device; lk; lk = lk->next)
		lk_sl_emergency_ceases(lk);
}

/*
 *  Routing marks the signalling link set whenever it applies or removes
 *  traffic from the link set.  If any routes in a signalling route set are
 *  providing traffic the the link set this flag is set.  When the signalling
 *  link is not used by any other routes, the flag is cleared.  This is used
 *  by changeover to determine whether instant diversion is possible.
 */
static inline void ls_traffic_starts(ls_t * ls)
{
	ls->statem.flags |= LS_FLAGS_TRAFFIC;
}
static inline void ls_traffic_ends(ls_t * ls)
{
	ls->statem.flags &= ~LS_FLAGS_TRAFFIC;
}

/*
 *  Routing marks the signalling link set when the adjacent signalling point
 *  is no longer accessible by any other routes.  (Note that when routing has
 *  failed, adjacent SP accessibility is dependent solely upon linkset
 *  availablity.  FIXME: the former needs implementation.  This is used by
 *  link management to determine whether inhibited signalling links should be
 *  used for signalling link management messages, and also used by signalling
 *  traffic management to determine whether time-controlled procedures are
 *  applicable.
 */
static inline void ls_adjacent_sp_inaccessible(ls_t * ls)
{
	ls->statem.flags |= LS_FLAGS_ADJ_INACCESSIBLE;
}
static inline void ls_adjacent_sp_accessible(ls_t * ls)
{
	ls->statem.flags &= ~LS_FLAGS_ADJ_INACCESSIBLE;
}

#endif				/* __SLS_LK_SM_H__ */


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/slsi/sls_sm.h

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: