From fa8ada01454fca86f6ce8f278c60fcd392a6ede4 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 16 Jan 2012 15:59:45 +0100 Subject: MTP3: Add SLTC gen_fsm implementation (Q.707) --- src/mtp3_sltc.erl | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/mtp3_sltc.erl diff --git a/src/mtp3_sltc.erl b/src/mtp3_sltc.erl new file mode 100644 index 0000000..2f52301 --- /dev/null +++ b/src/mtp3_sltc.erl @@ -0,0 +1,211 @@ +% MTP3 Signalling Link Test Control (SLTC) according to Q.707 + +% (C) 2011-2012 by Harald Welte +% +% All Rights Reserved +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU Affero General Public License as +% published by the Free Software Foundation; either version 3 of the +% License, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU Affero General Public License +% along with this program. If not, see . + +-module(mtp3_sltc). +-author('Harald Welte '). +-behaviour(gen_fsm). + +-include("mtp3.hrl"). + +% gen_fsm exports +-export([init/1, terminate/3, code_change/4, handle_event/3, handle_info/3]). + +% individual FSM states +-export([idle/2, first_attempt/2, second_attempt/2]). + +-record(sltc_state, { + hmrt_pid, + mgmt_pid, + lsac_pid, + sls, + opc, + adj_dpc, + t1, + t1_timeout, + x + }). + +-define(SLTC_T1_DEF, 10000). +-define(SLTC_T2_DEF, 60000). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% gen_fsm callbacks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init([Hmrt, Mgmt, Lsac, Sls, AdjDpc, Opc]) when + is_pid(Hmrt), is_pid(Mgmt), is_pid(Lsac), is_integer(Sls) -> + SltState = #sltc_state{hmrt_pid = Hmrt, + mgmt_pid = Mgmt, + lsac_pid = Lsac, + sls = Sls, + adj_dpc = AdjDpc, + opc = Opc, + t1_timeout = ?SLTC_T1_DEF, + x = 16#2342}, + {ok, idle, SltState}. + +terminate(Reason, State, _LoopDat) -> + io:format("Terminating ~p in State ~p (Reason: ~p)~n", + [?MODULE, State, Reason]), + ok. + +code_change(_OldVsn, StateName, LoopDat, _Extra) -> + {ok, StateName, LoopDat}. + +handle_event(Event, State, LoopDat) -> + io:format("Unknown Event ~p in state ~p~n", [Event, State]), + {next_state, State, LoopDat}. + +handle_info(Info, State, LoopDat) -> + io:format("Unknown Info ~p in state ~p~n", [Info, State]), + {next_state, State, LoopDat}. + +% See Figure 2 of Q.707 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% STATE: idle +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +idle(M=#mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTM}}, LoopDat) -> + Slta = slta_from_sltm(M), + send_to(hmrt, Slta, LoopDat), + {next_state, idle, LoopDat}; + +idle(start, LoopDat) -> + Sltm = generate_sltm(LoopDat), + send_to(hmrt, Sltm, LoopDat), + {ok, T1} = timer:apply_after(gen_fsm, send_event, + [self(), {timer_expired, t1}]), + {next_state, first_attempt, LoopDat#sltc_state{t1 = T1}}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% STATE: first_attempt +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +first_attempt(M=#mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTM}}, LoopDat) -> + Slta = slta_from_sltm(M), + send_to(hmrt, Slta, LoopDat), + {next_state, first_attempt, LoopDat}; + +first_attempt(M = #mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTA}}, LoopDat) -> + timer:cancel(LoopDat#sltc_state.t1), + case slt_matches(M, LoopDat) of + true -> + send_to(lsac, slt_successful, LoopDat), + {next_state, idle, LoopDat}; + false -> + Sltm = generate_sltm(LoopDat), + send_to(hmrt, Sltm, LoopDat), + {ok, T1} = timer:apply_after(gen_fsm, send_event, + [self(), {timer_expired, t1}]), + {next_state, second_attempt, LoopDat#sltc_state{t1 = T1}} + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% STATE: second_attempt +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +second_attempt(M=#mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTM}}, LoopDat) -> + Slta = slta_from_sltm(M), + send_to(hmrt, Slta, LoopDat), + {next_state, second_attempt, LoopDat}; + +second_attempt(M = #mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTA}}, LoopDat) -> + timer:cancel(LoopDat#sltc_state.t1), + case slt_matches(M, LoopDat) of + true -> + send_to(lsac, slt_successful, LoopDat); + false -> + send_to(mgmt, slt_failed, LoopDat), + send_to(lsac, slt_failed, LoopDat) + end, + {next_state, idle, LoopDat}; + +second_attempt({timer_expired, t1}, LoopDat) -> + send_to(mgmt, slt_failed, LoopDat), + send_to(lsac, slt_failed, LoopDat), + {next_state, idle, LoopDat}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% helper functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +send_to(hmrt, What, #sltc_state{hmrt_pid = Txc}) -> + Txc ! {sltc_hmrt, What}; +send_to(mgmt, What, #sltc_state{mgmt_pid = Txc}) -> + Txc ! {sltc_mgmt, What}; +send_to(lsac, What, #sltc_state{lsac_pid = Txc}) -> + Txc ! {sltc_lsac, What}. + +slta_from_sltm(M = #mtp3_msg{service_ind = ?MTP3_SERV_MGMT, + routing_label = RoutLbl, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTM, + test_pattern = TP}}) -> + InvRoutLbl = invert_rout_lbl(RoutLbl), + M#mtp3_msg{routing_label = InvRoutLbl, + payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, + h1 = ?MTP3MG_H1_SLTA, + test_pattern = TP}}. + +generate_sltm(LoopDat) -> + Mg = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, h1 = ?MTP3MG_H1_SLTM, + test_pattern = LoopDat#sltc_state.x}, + Lbl = #mtp3_routing_label{sig_link_sel = LoopDat#sltc_state.sls, + origin_pc = LoopDat#sltc_state.opc, + dest_pc = LoopDat#sltc_state.adj_dpc}, + + #mtp3_msg{network_ind = ?MTP3_NETIND_INTERNATIONAL, + service_ind = ?MTP3_SERV_MGMT, + routing_label = Lbl, payload = Mg}. + +rout_lbl_matches(#mtp3_routing_label{sig_link_sel = SlsLocal, + origin_pc = OPC}, LoopDat) -> + #sltc_state{adj_dpc = AdjDpc, sls = SLS} = LoopDat, + if SLS == SlsLocal, OPC == AdjDpc -> + true; + true -> + false + end. + +slt_matches(#mtp3_msg{routing_label = RoutLbl, + payload = #mtp3mg_msg{test_pattern = TP}}, LoopDat) -> + case LoopDat#sltc_state.x of + TP -> + rout_lbl_matches(RoutLbl, LoopDat); + _ -> + false + end. + + + +invert_rout_lbl(L = #mtp3_routing_label{origin_pc = Opc, dest_pc = Dpc}) -> + L#mtp3_routing_label{origin_pc = Dpc, dest_pc = Opc}. -- cgit v1.2.3