OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
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__ */
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |