blob: d23aaf03a1a31f85e5014329787bd59c8c872079 [file] [log] [blame]
Harald Welteb6689882012-01-16 16:00:45 +01001% MTP2 Initial Alignment Control according to Q.703 Figure 4 / Figure 9
2
3% (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
4%
5% All Rights Reserved
6%
7% This program is free software; you can redistribute it and/or modify
8% it under the terms of the GNU Affero General Public License as
9% published by the Free Software Foundation; either version 3 of the
10% License, or (at your option) any later version.
11%
12% This program is distributed in the hope that it will be useful,
13% but WITHOUT ANY WARRANTY; without even the implied warranty of
14% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15% GNU General Public License for more details.
16%
17% You should have received a copy of the GNU Affero General Public License
18% along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20-module(mtp2_iac).
21-author('Harald Welte <laforge@gnumonks.org>').
22-behaviour(gen_fsm).
23
24% gen_fsm exports
25-export([init/1, terminate/3, code_change/4, handle_event/3, handle_info/3]).
26
27% states in this FSM
28-export([idle/2, not_aligned/2, aligned/2, proving/2]).
29
30% Timeouts in milliseconds According to Q.703 / Section 12.3
31-define(M2PA_T1_DEF, 50000).
32-define(M2PA_T2_DEF, 150000).
33-define(M2PA_T3_DEF, 2000).
34-define(M2PA_T4N_DEF, 8200).
35-define(M2PA_T4E_DEF, 500).
36
37-record(iac_state, {
38 t2_timeout,
39 t3_timeout,
40 t4_timeout,
41 t4_timeout_pn,
42 t4_timeout_pe,
43 t2, t3, t4,
44 emergency,
45 cp,
46 further_prov,
47 lsc_pid,
48 aerm_pid,
49 txc_pid
50 }).
51
52%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53% gen_fsm callbacks
54%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55
56init([Lsc, Aerm, Txc]) ->
57 IacState = #iac_state{t2_timeout = ?M2PA_T2_DEF,
58 t3_timeout = ?M2PA_T3_DEF,
59 t4_timeout_pn = ?M2PA_T4N_DEF,
60 t4_timeout_pe = ?M2PA_T4E_DEF,
61 emergency = 0,
62 cp = 0,
63 further_prov = 1,
64 lsc_pid = Lsc,
65 aerm_pid = Aerm,
66 txc_pid = Txc},
67 {ok, idle, IacState}.
68
69terminate(Reason, State, _LoopDat) ->
70 io:format("Terminating ~p in State ~p (Reason: ~p)~n",
71 [?MODULE, State, Reason]),
72 ok.
73
74code_change(_OldVsn, StateName, LoopDat, _Extra) ->
75 {ok, StateName, LoopDat}.
76
77handle_event(Event, State, LoopDat) ->
78 io:format("Unknown Event ~p in state ~p~n", [Event, State]),
79 {next_state, State, LoopDat}.
80
81
82handle_info(Info, State, LoopDat) ->
83 io:format("Unknown Info ~p in state ~p~n", [Info, State]),
84 {next_state, State, LoopDat}.
85
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87% STATE "idle"
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
90idle(start, LoopDat) ->
91 % send sio
92 send_to_txc(si_o, LoopDat),
93 % start timer
94 T2tout = LoopDat#iac_state.t2_timeout,
95 {ok, T2} = timer:apply_after(T2tout, gen_fsm, send_event,
96 [self(), {timer_expired, t2}]),
97 {next_state, not_aligned, LoopDat#iac_state{t2 = T2}};
98idle(emergency, LoopDat) ->
99 % mark emergency
100 {next_state, idle, LoopDat#iac_state{emergency = 1}}.
101
102
103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104% STATE "not aligned"
105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106
107not_aligned(stop, LoopDat) ->
108 % stop T2
109 timer:cancel(LoopDat#iac_state.t2),
110 % cancel emergency
111 {next_state, idle, LoopDat#iac_state{emergency=0}};
112not_aligned(si_e, LoopDat) ->
113 % stop T2
114 timer:cancel(LoopDat#iac_state.t2),
115 T4tout = LoopDat#iac_state.t4_timeout_pe,
116 % send SIE or SIN
117 case LoopDat#iac_state.emergency of
118 0 ->
119 Send = si_n;
120 _ ->
121 Send = si_e
122 end,
123 send_to_txc(Send, LoopDat),
124 % start T3
125 T3tout = LoopDat#iac_state.t3_timeout,
126 {ok, T3} = timer:apply_after(T3tout, gen_fsm, send_event,
127 [self(), {timer_expired, t3}]),
128 {next_state, aligned, LoopDat#iac_state{t3 = T3, t2 = undefined, t4_timeout = T4tout}};
129not_aligned(What, LoopDat) when What == si_o; What == si_n ->
130 % stop T2
131 timer:cancel(LoopDat#iac_state.t2),
132 % send SIE or SIN
133 case LoopDat#iac_state.emergency of
134 0 ->
135 T4tout = LoopDat#iac_state.t4_timeout_pn,
136 Send = si_n;
137 _ ->
138 T4tout = LoopDat#iac_state.t4_timeout_pe,
139 Send = si_e
140 end,
141 send_to_txc(Send, LoopDat),
142 T3tout = LoopDat#iac_state.t3_timeout,
143 {ok, T3} = timer:apply_after(T3tout, gen_fsm, send_event,
144 [self(), {timer_expired, t3}]),
145 {next_state, aligned, LoopDat#iac_state{t3 = T3, t2 = undefined, t4_timeout = T4tout}};
146not_aligned(emergency, LoopDat) ->
147 % mark emergency
148 {next_state, not_aligned, LoopDat#iac_state{emergency=1}};
149not_aligned({timer_expired, t2}, LoopDat) ->
150 % send 'alignment not possible' to LSC
151 send_to_lsc(alignment_not_possible, LoopDat),
152 % stop emergency
153 {next_state, idle, LoopDat#iac_state{emergency=0}}.
154
155
156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157% STATE "aligned"
158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159aligned(What, LoopDat) when What == si_n; What == si_e ->
160 case What of
161 si_e ->
162 % set T4 to Pe
163 T4tout = LoopDat#iac_state.t4_timeout_pe;
164 _ ->
165 T4tout = LoopDat#iac_state.t4_timeout_pn
166 end,
167 % stop T3
168 timer:cancel(LoopDat#iac_state.t3),
169 ToutPE = LoopDat#iac_state.t4_timeout_pe,
170 case T4tout of
171 ToutPE ->
172 % set i to ie IAC->AERM
173 send_to_aerm(set_i_to_ie, LoopDat);
174 _ ->
175 ok
176 end,
177 % send Start to AERM
178 send_to_aerm(start, LoopDat),
179 % start T4
180 io:format("trying to start T4, T4tout=~p~n", [T4tout]),
181 {ok, T4} = timer:apply_after(T4tout, gen_fsm, send_event,
182 [self(), {timer_expired, t4}]),
183 % Cp := 0
184 % cancel further proving?
185 LoopDat2 = LoopDat#iac_state{t4 = T4, t4_timeout = T4tout,
186 cp = 0, further_prov = 0},
187 {next_state, proving, LoopDat2};
188aligned(emergency, LoopDat) ->
189 % Send SIE
190 send_to_txc(si_e, LoopDat),
191 T4tout = LoopDat#iac_state.t4_timeout_pe,
192 {next_State, aligned, LoopDat#iac_state{t4_timeout = T4tout}};
193aligned(si_os, LoopDat) ->
194 % Send alignment not possible
195 send_to_lsc(alignment_not_possible, LoopDat),
196 % stop T3
197 timer:cancel(LoopDat#iac_state.t3),
198 {next_state, idle, LoopDat#iac_state{emergency=0, t3=undefined}};
199aligned(stop, LoopDat) ->
200 % Stop T3
201 timer:cancel(LoopDat#iac_state.t3),
202 % cancel Emergency
203 {next_state, idle, LoopDat#iac_state{emergency=0, t3=undefined}};
204aligned({timer_expired, t3}, LoopDat) ->
205 % Send alignment not possible
206 send_to_lsc(alignment_not_possible, LoopDat),
207 % cancel emergency
208 {next_state, idle, LoopDat#iac_state{emergency=0}}.
209
210
211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212% STATE "proving"
213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214fig9_4(LoopDat) ->
215 % send Stop to AERM
216 send_to_aerm(stop, LoopDat),
217 % cancel emergency
218 {next_state, idle, LoopDat#iac_state{emergency=0}}.
219
220fig9_5(LoopDat) ->
221 % send Start to AERM
222 send_to_aerm(start, LoopDat),
223 % cancel further proving
224 % start T4
225 T4tout = LoopDat#iac_state.t4_timeout,
226 {ok, T4} = timer:apply_after(T4tout, gen_fsm, send_event,
227 [self(), {timer_expired, t4}]),
228 {next_state, proving, LoopDat#iac_state{t4=T4, further_prov=0}}.
229
230prov_emerg_or_sie(LoopDat) ->
231 % stop T4
232 timer:cancel(LoopDat#iac_state.t4),
233 % Set T4 to Pe
234 T4tout = LoopDat#iac_state.t4_timeout_pe,
235 % Send stop to AERM
236 send_to_aerm(stop, LoopDat),
237 % Send 'set ti to tie' to AERM
238 send_to_aerm(set_ti_to_tie, LoopDat),
239 fig9_5(LoopDat#iac_state{t4_timeout=T4tout, t4=undefined}).
240
241
242proving(expires, LoopDat) ->
243 % alignment complete
244 {next_state, idle, LoopDat};
245proving(si_e, LoopDat) ->
246 ToutPE = LoopDat#iac_state.t4_timeout_pe,
247 case LoopDat#iac_state.t4_timeout of
248 ToutPE ->
249 {next_state, proving, LoopDat};
250 _ ->
251 prov_emerg_or_sie(LoopDat)
252 end;
253proving(emergency, LoopDat) ->
254 prov_emerg_or_sie(LoopDat);
255proving(stop, LoopDat) ->
256 % stop T4
257 timer:cancel(LoopDat#iac_state.t4),
258 fig9_4(LoopDat);
259proving(si_os, LoopDat) ->
260 % stop T4
261 timer:cancel(LoopDat#iac_state.t4),
262 % Send alignment not possible to LSC
263 send_to_lsc(alignment_not_possible, LoopDat),
264 fig9_4(LoopDat);
265proving(high_err_rate, LoopDat) ->
266 % alignment not possible
267 {next_state, idle, LoopDat};
268proving(sio, LoopDat) ->
269 % stop T4
270 timer:cancel(LoopDat#iac_state.t4),
271 % send Stop to AERM
272 send_to_aerm(stop, LoopDat),
273 % start T3
274 T3tout = LoopDat#iac_state.t3_timeout,
275 {ok, T3} = timer:apply_after(T3tout, gen_fsm, send_event,
276 [self(), {timer_expired, t3}]),
277 {next_state, aligned, LoopDat#iac_state{t3=T3, t4=undefined}};
278proving(What, LoopDat) when What == correct_su; What == si_n ->
279 case LoopDat#iac_state.further_prov of
280 1 ->
281 % stop T4
282 timer:cancel(LoopDat#iac_state.t4),
283 fig9_5(LoopDat);
284 _ ->
285 {next_state, proving, LoopDat}
286 end;
287proving({timer_expired, t4}, LoopDat) ->
288 % check if we are further proving, if yes, call fig9_5
289 case LoopDat#iac_state.further_prov of
290 1 ->
291 fig9_5(LoopDat);
292 _ ->
293 % send 'aligment complete' to LSC
294 send_to_lsc(alignment_complete, LoopDat),
295 fig9_4(LoopDat)
296 end;
297proving(abort_proving, LoopDat) ->
298 % Cp := Cp + 1
299 Cp = LoopDat#iac_state.cp,
300 LoopDat2 = LoopDat#iac_state{cp = Cp + 1},
301 case Cp + 1 of
302 5 ->
303 % send 'alignment not possible' to LSC
304 send_to_lsc(alignment_not_possible, LoopDat),
305 % stop T4
306 timer:cancel(LoopDat#iac_state.t4),
307 fig9_4(LoopDat2);
308 _ ->
309 % mark further proving
310 {next_state, proving, LoopDat2#iac_state{further_prov=1}}
311 end.
312
313
314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315% helper functions
316%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317send_to_lsc(What, #iac_state{lsc_pid = Lsc}) ->
318 gen_fsm:send_event(Lsc, What).
319
320send_to_aerm(What, #iac_state{aerm_pid = Aerm}) ->
321 Aerm ! {iac_aerm, What}.
322
323send_to_txc(What, #iac_state{txc_pid = Txc}) ->
324 Txc ! {iac_txc, What}.