Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / i4b / isdnd / fsm.c
1 /*
2  * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *---------------------------------------------------------------------------
26  *
27  *      FSM for isdnd
28  *      -------------
29  *
30  * $FreeBSD: src/usr.sbin/i4b/isdnd/fsm.c,v 1.6.2.2 2001/08/10 23:11:14 obrien Exp $
31  *
32  *      last edit-date: [Sat Jul 21 18:25:48 2001]
33  *
34  *---------------------------------------------------------------------------*/
35
36 #include "isdnd.h"
37
38 /* table of state descriptions */
39
40 static char *state_text[N_STATES] = {
41         "idle",
42         "dialing",
43         "waitdialretry",
44         "dialretry",
45         
46         "pcb-dialing",
47         "pcb-dialfail",
48         "pcb-waitcall",
49
50         "acb-waitdisc",
51         "acb-waitdial",
52         "acb-dialing",
53         "acb-dialfail",
54
55         "accepted",
56         "connected",
57         "waitdisconnect",       
58         "down",
59         "alert",
60
61         "Illegal State" 
62 };
63
64 /* table of event descriptions */
65
66 static char *event_text[N_EVENTS] = {
67
68         /* incoming messages */
69         
70         "msg-con-ind",
71         "msg-con-act-ind",
72         "msg-disc-ind",
73         "msg-dialout",
74
75         /* local events */
76         
77         "timeout",
78         "disconnect-req",
79         "callback-req",
80         "alert-req",
81         
82         /* illegal */
83         
84         "Illegal Event"
85 };
86
87 /*---------------------------------------------------------------------------*
88  *      illegal state default action
89  *---------------------------------------------------------------------------*/ 
90 static void
91 F_ill(cfg_entry_t *cep)
92 {
93         DBGL(DL_STATE, (log(LL_DBG, "F_ill: Illegal State reached !!!")));
94 }
95
96 /*---------------------------------------------------------------------------*
97  *      No change, No action
98  *---------------------------------------------------------------------------*/ 
99 static void
100 F_NcNa(cfg_entry_t *cep)
101 {
102 }
103
104 /*---------------------------------------------------------------------------*
105  *      incoming CONNECT, accepting call
106  *---------------------------------------------------------------------------*/ 
107 static void
108 F_MCI(cfg_entry_t *cep)
109 {
110         DBGL(DL_STATE, (log(LL_DBG, "F_MCI: tx SETUP_RESP_ACCEPT")));
111         sendm_connect_resp(cep, cep->cdid, SETUP_RESP_ACCEPT, 0);
112         start_timer(cep, TIMEOUT_CONNECT_ACTIVE);
113 }
114
115 /*---------------------------------------------------------------------------*
116  *      incoming connect active, call is now active
117  *---------------------------------------------------------------------------*/ 
118 static void
119 F_MCAI(cfg_entry_t *cep)
120 {
121         DBGL(DL_STATE, (log(LL_DBG, "F_MCAI: Connection active!")));
122
123         stop_timer(cep);
124
125         if((cep->dialin_reaction == REACT_ANSWER) &&
126            (cep->b1protocol == BPROT_NONE))
127         {
128                 exec_answer(cep);
129         }
130 }
131
132 /*---------------------------------------------------------------------------*
133  *      timeout
134  *---------------------------------------------------------------------------*/ 
135 static void
136 F_TIMO(cfg_entry_t *cep)
137 {
138         DBGL(DL_STATE, (log(LL_DBG, "F_TIMO: Timout occured!")));
139         sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
140         cep->cdid = CDID_UNUSED;        
141 }
142
143 /*---------------------------------------------------------------------------*
144  *      incoming disconnect indication
145  *---------------------------------------------------------------------------*/ 
146 static void
147 F_IDIS(cfg_entry_t *cep)
148 {
149         DBGL(DL_STATE, (log(LL_DBG, "F_IDIS: disconnect indication")));
150         cep->cdid = CDID_UNUSED;
151 }
152
153 /*---------------------------------------------------------------------------*
154  *      local disconnect request
155  *---------------------------------------------------------------------------*/ 
156 static void
157 F_DRQ(cfg_entry_t *cep)
158 {
159         DBGL(DL_STATE, (log(LL_DBG, "F_DRQ: local disconnect request")));
160         sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
161 }
162
163 /*---------------------------------------------------------------------------*
164  *      disconnect indication after local disconnect req
165  *---------------------------------------------------------------------------*/ 
166 static void
167 F_MDI(cfg_entry_t *cep)
168 {
169         DBGL(DL_STATE, (log(LL_DBG, "F_MDI: disconnect indication, local disconnected")));
170         cep->cdid = CDID_UNUSED;
171 }
172
173 /*---------------------------------------------------------------------------*
174  *      local requested outgoing dial
175  *---------------------------------------------------------------------------*/ 
176 static void
177 F_DIAL(cfg_entry_t *cep)
178 {
179         DBGL(DL_STATE, (log(LL_DBG, "F_DIAL: local dial out request")));
180
181         if(cep->dialrandincr)
182                 cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
183
184         cep->dial_count = 0;
185                 
186         select_first_dialno(cep);
187
188         sendm_connect_req(cep);
189 }
190
191 /*---------------------------------------------------------------------------*
192  *      outgoing dial successfull
193  *---------------------------------------------------------------------------*/ 
194 static void
195 F_DOK(cfg_entry_t *cep)
196 {
197         DBGL(DL_STATE, (log(LL_DBG, "F_DOK: dial out ok")));
198         select_this_dialno(cep);
199 }
200
201 /*---------------------------------------------------------------------------*
202  *      outgoing dial fail (ST_SUSE !!!)
203  *---------------------------------------------------------------------------*/ 
204 static void
205 F_DFL(cfg_entry_t *cep)
206 {
207         cep->last_release_time = time(NULL);
208         
209         if(cep->dialouttype == DIALOUT_NORMAL)
210         {
211                 cep->dial_count++;
212         
213                 if(cep->dial_count < cep->dialretries || cep->dialretries == -1) /* Added by FST <mailto:fsteevie@dds.nl> for unlimited dialing (sorry, but I needed it) */
214                 {
215                         /* inside normal retry cycle */
216                 
217                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial fail, dial retry")));
218                         select_next_dialno(cep);
219                         cep->cdid = CDID_RESERVED;
220                         cep->state = ST_DIALRTMRCHD;
221                         return;
222                 }
223         
224                 /* retries exhausted */
225                 
226                 if(!cep->usedown)
227                 {
228                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry fail, dial retries exhausted")));
229                         dialresponse(cep, DSTAT_TFAIL);
230                         cep->cdid = CDID_UNUSED;
231                         cep->dial_count = 0;
232                         cep->state = ST_IDLE;
233                         return;
234                 }
235         
236                 /* interface up/down active */
237         
238                 cep->down_retry_count++;
239         
240                 if(cep->down_retry_count > cep->downtries)
241                 {
242                         /* set interface down */
243                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, setting interface down!")));
244                         dialresponse(cep, DSTAT_PFAIL);
245                         if_down(cep);                                   
246                         cep->state = ST_DOWN;
247                 }
248                 else
249                 {
250                         /* enter new dial retry cycle */
251                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, enter new retry cycle!")));
252                         select_next_dialno(cep);
253                         cep->state = ST_DIALRTMRCHD;
254                 }
255         
256                 cep->dial_count = 0;
257                 cep->cdid = CDID_RESERVED;
258         }
259         else    /* cdp->dialouttype == DIALOUT_CALLEDBACK */
260         {
261                 DBGL(DL_STATE, (log(LL_DBG, "F_DFL: calledback dial done, wait for incoming call")));
262                 cep->cdid = CDID_RESERVED;
263                 cep->state = ST_PCB_WAITCALL;
264         }
265 }
266
267 /*---------------------------------------------------------------------------*
268  *      local requested outgoing dial
269  *---------------------------------------------------------------------------*/ 
270 static void
271 F_ACBW(cfg_entry_t *cep)
272 {
273         DBGL(DL_STATE, (log(LL_DBG, "F_ACBW: local callback, wait callback recovery time")));
274
275         if(cep->dialrandincr)
276                 cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
277
278         cep->dial_count = 0;
279
280         cep->cdid = CDID_RESERVED;
281 }
282
283 /*---------------------------------------------------------------------------*
284  *      active callback dialout retry (ST_SUSE !!!)
285  *---------------------------------------------------------------------------*/ 
286 static void
287 F_ACBR(cfg_entry_t *cep)
288 {       
289         cep->dial_count++;
290
291         if(cep->dial_count < cep->dialretries || cep->dialretries == -1) /* Added by FST <mailto:fsteevie@dds.nl> for unlimited dialing (sorry, but I needed it) */
292         {
293                 /* inside normal retry cycle */
294         
295                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial fail, dial retry")));
296                 select_next_dialno(cep);
297                 cep->cdid = CDID_RESERVED;
298                 cep->state = ST_ACB_DIALFAIL;
299                 return;
300         }
301
302         /* retries exhausted */
303         
304         if(!cep->usedown)
305         {
306                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry fail, dial retries exhausted")));
307                 dialresponse(cep, DSTAT_TFAIL);
308                 cep->cdid = CDID_UNUSED;
309                 cep->dial_count = 0;
310                 cep->state = ST_IDLE;
311                 return;
312         }
313
314         /* interface up/down active */
315
316         cep->down_retry_count++;
317
318         if(cep->down_retry_count > cep->downtries)
319         {
320                 /* set interface down */
321                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, setting interface down!")));
322                 dialresponse(cep, DSTAT_PFAIL);
323                 if_down(cep);
324                 cep->state = ST_DOWN;
325         }
326         else
327         {
328                 /* enter new dial retry cycle */
329                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, enter new retry cycle!")));
330                 select_next_dialno(cep);
331                 cep->state = ST_ACB_DIALFAIL;
332         }
333
334         cep->dial_count = 0;
335         cep->cdid = CDID_RESERVED;      
336 }
337
338 /*---------------------------------------------------------------------------*
339  *      local requested to send ALERT message
340  *---------------------------------------------------------------------------*/ 
341 static void
342 F_ALRT(cfg_entry_t *cep)
343 {
344         DBGL(DL_STATE, (log(LL_DBG, "F_ALRT: local send alert request")));
345
346         cep->alert_time = cep->alert;
347         
348         sendm_alert_req(cep);
349 }
350
351 /*---------------------------------------------------------------------------*
352  *      isdn daemon state transition table
353  *---------------------------------------------------------------------------*/ 
354 struct state_tab {
355         void(*func)(cfg_entry_t *cep);          /* function to execute */
356         int newstate;                           /* next state */
357 } state_tab[N_EVENTS][N_STATES] = {
358
359 /* STATE:       ST_IDLE                 ST_DIAL                 ST_DIALRTMRCHD          ST_DIALRETRY            ST_PCB_DIAL             ST_PCB_DIALFAIL         ST_PCB_WAITCALL         ST_ACB_WAITDISC         ST_ACB_WAITDIAL         ST_ACB_DIAL             ST_ACB_DIALFAIL         ST_ACCEPTED             ST_CONNECTED            ST_WAITDISCI            ST_DOWN                 ST_ALERT                ST_ILLEGAL              */
360 /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
361 /* messages */
362 /* EV_MCI   */{{F_MCI, ST_ACCEPTED},    {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_MCI, ST_ACCEPTED},   {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_MCI, ST_ACCEPTED},   {F_ill, ST_ILL}},
363 /* EV_MCAI  */{{F_ill, ST_ILL},         {F_DOK, ST_CONNECTED},  {F_ill, ST_ILL},        {F_DOK, ST_CONNECTED},  {F_DOK, ST_CONNECTED},  {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_DOK, ST_CONNECTED},  {F_ill, ST_ILL},        {F_MCAI,ST_CONNECTED},  {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}},
364 /* EV_MDI   */{{F_ill, ST_ILL},         {F_DFL, ST_SUSE},       {F_ill, ST_ILL},        {F_DFL, ST_SUSE},       {F_DFL, ST_SUSE},       {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ACBW,ST_ACB_WAITDIAL},{F_ill, ST_ILL},       {F_ACBR, ST_SUSE},      {F_ACBR,ST_SUSE},       {F_IDIS,ST_IDLE},       {F_IDIS,ST_IDLE},       {F_MDI, ST_IDLE},       {F_ill, ST_ILL},        {F_MDI, ST_IDLE},       {F_ill, ST_ILL}},
365 /* EV_MDO   */{{F_DIAL,ST_DIAL},        {F_NcNa,ST_DIAL},       {F_NcNa,ST_DIALRTMRCHD},{F_NcNa,ST_DIALRETRY},  {F_NcNa,ST_PCB_DIAL},   {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}},
366
367 /* local requests */
368 /* EV_TIMO  */{{F_ill, ST_ILL},         {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_TIMO,ST_IDLE},       {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}},
369 /* EV_DRQ   */{{F_NcNa, ST_IDLE},               {F_DRQ, ST_WAITDISCI},  {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_DRQ, ST_WAITDISCI},  {F_NcNa,ST_WAITDISCI},  {F_NcNa, ST_DOWN},      {F_ill, ST_ILL},        {F_ill, ST_ILL}},
370 /* EV_CBRQ  */{{F_NcNa,ST_ACB_WAITDIAL},{F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},       {F_NcNa,ST_ACB_WAITDIAL},{F_NcNa, ST_ACB_DIAL}, {F_NcNa,ST_ACB_DIALFAIL},{F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}},
371 /* EV_ALRT  */{{F_ALRT,ST_ALERT},       {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}},
372
373 /* illegal  */
374
375 /* EV_ILL   */{{F_ill, ST_ILL},         {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL},        {F_ill, ST_ILL}}
376 };
377
378 /*---------------------------------------------------------------------------*
379  *      event handler
380  *---------------------------------------------------------------------------*/ 
381 void
382 next_state(cfg_entry_t *cep, int event)
383 {
384         int currstate, newstate;
385
386         if(event > N_EVENTS)
387         {
388                 log(LL_ERR, "next_state: event > N_EVENTS");
389                 error_exit(1, "next_state: event > N_EVENTS");
390         }
391
392         currstate = cep->state;
393
394         if(currstate > N_STATES)
395         {
396                 log(LL_ERR, "next_state: currstate > N_STATES");
397                 error_exit(1, "next_state: currstate > N_STATES");
398         }
399
400         newstate = state_tab[event][currstate].newstate;
401
402         if(newstate > N_STATES)
403         {
404                 log(LL_ERR, "next_state: newstate > N_STATES");
405                 error_exit(1, "next_state: newstate > N_STATES");
406         }
407
408         if(newstate != ST_SUSE)
409         {
410                 DBGL(DL_STATE, (log(LL_DBG, "FSM event [%s]: [%s => %s]", event_text[event],
411                                 state_text[currstate],
412                                 state_text[newstate])));
413         }
414
415         (*state_tab[event][currstate].func)(cep);
416
417         if(newstate == ST_ILL)
418         {
419                 log(LL_ERR, "FSM ILLEGAL STATE, event=%s: oldstate=%s => newstate=%s]",
420                                 event_text[event],
421                                 state_text[currstate],
422                                 state_text[newstate]);
423         }
424
425         if(newstate == ST_SUSE)
426         {
427                 DBGL(DL_STATE, (log(LL_DBG, "FSM (SUSE) event [%s]: [%s => %s]", event_text[event],
428                                 state_text[currstate],
429                                 state_text[cep->state])));
430         }
431         else
432         {
433                 cep->state = newstate;
434         }
435 }
436
437 /*---------------------------------------------------------------------------*
438  *      return pointer to current state description
439  *---------------------------------------------------------------------------*/ 
440 char *
441 printstate(cfg_entry_t *cep)
442 {
443         return((char *) state_text[cep->state]);
444 }
445
446 /* EOF */