483b700cb641afab63d1287dd82e5b2e5e7583bb
[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  * $DragonFly: src/usr.sbin/i4b/isdnd/fsm.c,v 1.2 2003/06/17 04:29:54 dillon Exp $
32  *
33  *      last edit-date: [Sat Jul 21 18:25:48 2001]
34  *
35  *---------------------------------------------------------------------------*/
36
37 #include "isdnd.h"
38
39 /* table of state descriptions */
40
41 static char *state_text[N_STATES] = {
42         "idle",
43         "dialing",
44         "waitdialretry",
45         "dialretry",
46         
47         "pcb-dialing",
48         "pcb-dialfail",
49         "pcb-waitcall",
50
51         "acb-waitdisc",
52         "acb-waitdial",
53         "acb-dialing",
54         "acb-dialfail",
55
56         "accepted",
57         "connected",
58         "waitdisconnect",       
59         "down",
60         "alert",
61
62         "Illegal State" 
63 };
64
65 /* table of event descriptions */
66
67 static char *event_text[N_EVENTS] = {
68
69         /* incoming messages */
70         
71         "msg-con-ind",
72         "msg-con-act-ind",
73         "msg-disc-ind",
74         "msg-dialout",
75
76         /* local events */
77         
78         "timeout",
79         "disconnect-req",
80         "callback-req",
81         "alert-req",
82         
83         /* illegal */
84         
85         "Illegal Event"
86 };
87
88 /*---------------------------------------------------------------------------*
89  *      illegal state default action
90  *---------------------------------------------------------------------------*/ 
91 static void
92 F_ill(cfg_entry_t *cep)
93 {
94         DBGL(DL_STATE, (log(LL_DBG, "F_ill: Illegal State reached !!!")));
95 }
96
97 /*---------------------------------------------------------------------------*
98  *      No change, No action
99  *---------------------------------------------------------------------------*/ 
100 static void
101 F_NcNa(cfg_entry_t *cep)
102 {
103 }
104
105 /*---------------------------------------------------------------------------*
106  *      incoming CONNECT, accepting call
107  *---------------------------------------------------------------------------*/ 
108 static void
109 F_MCI(cfg_entry_t *cep)
110 {
111         DBGL(DL_STATE, (log(LL_DBG, "F_MCI: tx SETUP_RESP_ACCEPT")));
112         sendm_connect_resp(cep, cep->cdid, SETUP_RESP_ACCEPT, 0);
113         start_timer(cep, TIMEOUT_CONNECT_ACTIVE);
114 }
115
116 /*---------------------------------------------------------------------------*
117  *      incoming connect active, call is now active
118  *---------------------------------------------------------------------------*/ 
119 static void
120 F_MCAI(cfg_entry_t *cep)
121 {
122         DBGL(DL_STATE, (log(LL_DBG, "F_MCAI: Connection active!")));
123
124         stop_timer(cep);
125
126         if((cep->dialin_reaction == REACT_ANSWER) &&
127            (cep->b1protocol == BPROT_NONE))
128         {
129                 exec_answer(cep);
130         }
131 }
132
133 /*---------------------------------------------------------------------------*
134  *      timeout
135  *---------------------------------------------------------------------------*/ 
136 static void
137 F_TIMO(cfg_entry_t *cep)
138 {
139         DBGL(DL_STATE, (log(LL_DBG, "F_TIMO: Timout occured!")));
140         sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
141         cep->cdid = CDID_UNUSED;        
142 }
143
144 /*---------------------------------------------------------------------------*
145  *      incoming disconnect indication
146  *---------------------------------------------------------------------------*/ 
147 static void
148 F_IDIS(cfg_entry_t *cep)
149 {
150         DBGL(DL_STATE, (log(LL_DBG, "F_IDIS: disconnect indication")));
151         cep->cdid = CDID_UNUSED;
152 }
153
154 /*---------------------------------------------------------------------------*
155  *      local disconnect request
156  *---------------------------------------------------------------------------*/ 
157 static void
158 F_DRQ(cfg_entry_t *cep)
159 {
160         DBGL(DL_STATE, (log(LL_DBG, "F_DRQ: local disconnect request")));
161         sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
162 }
163
164 /*---------------------------------------------------------------------------*
165  *      disconnect indication after local disconnect req
166  *---------------------------------------------------------------------------*/ 
167 static void
168 F_MDI(cfg_entry_t *cep)
169 {
170         DBGL(DL_STATE, (log(LL_DBG, "F_MDI: disconnect indication, local disconnected")));
171         cep->cdid = CDID_UNUSED;
172 }
173
174 /*---------------------------------------------------------------------------*
175  *      local requested outgoing dial
176  *---------------------------------------------------------------------------*/ 
177 static void
178 F_DIAL(cfg_entry_t *cep)
179 {
180         DBGL(DL_STATE, (log(LL_DBG, "F_DIAL: local dial out request")));
181
182         if(cep->dialrandincr)
183                 cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
184
185         cep->dial_count = 0;
186                 
187         select_first_dialno(cep);
188
189         sendm_connect_req(cep);
190 }
191
192 /*---------------------------------------------------------------------------*
193  *      outgoing dial successfull
194  *---------------------------------------------------------------------------*/ 
195 static void
196 F_DOK(cfg_entry_t *cep)
197 {
198         DBGL(DL_STATE, (log(LL_DBG, "F_DOK: dial out ok")));
199         select_this_dialno(cep);
200 }
201
202 /*---------------------------------------------------------------------------*
203  *      outgoing dial fail (ST_SUSE !!!)
204  *---------------------------------------------------------------------------*/ 
205 static void
206 F_DFL(cfg_entry_t *cep)
207 {
208         cep->last_release_time = time(NULL);
209         
210         if(cep->dialouttype == DIALOUT_NORMAL)
211         {
212                 cep->dial_count++;
213         
214                 if(cep->dial_count < cep->dialretries || cep->dialretries == -1) /* Added by FST <mailto:fsteevie@dds.nl> for unlimited dialing (sorry, but I needed it) */
215                 {
216                         /* inside normal retry cycle */
217                 
218                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial fail, dial retry")));
219                         select_next_dialno(cep);
220                         cep->cdid = CDID_RESERVED;
221                         cep->state = ST_DIALRTMRCHD;
222                         return;
223                 }
224         
225                 /* retries exhausted */
226                 
227                 if(!cep->usedown)
228                 {
229                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry fail, dial retries exhausted")));
230                         dialresponse(cep, DSTAT_TFAIL);
231                         cep->cdid = CDID_UNUSED;
232                         cep->dial_count = 0;
233                         cep->state = ST_IDLE;
234                         return;
235                 }
236         
237                 /* interface up/down active */
238         
239                 cep->down_retry_count++;
240         
241                 if(cep->down_retry_count > cep->downtries)
242                 {
243                         /* set interface down */
244                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, setting interface down!")));
245                         dialresponse(cep, DSTAT_PFAIL);
246                         if_down(cep);                                   
247                         cep->state = ST_DOWN;
248                 }
249                 else
250                 {
251                         /* enter new dial retry cycle */
252                         DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, enter new retry cycle!")));
253                         select_next_dialno(cep);
254                         cep->state = ST_DIALRTMRCHD;
255                 }
256         
257                 cep->dial_count = 0;
258                 cep->cdid = CDID_RESERVED;
259         }
260         else    /* cdp->dialouttype == DIALOUT_CALLEDBACK */
261         {
262                 DBGL(DL_STATE, (log(LL_DBG, "F_DFL: calledback dial done, wait for incoming call")));
263                 cep->cdid = CDID_RESERVED;
264                 cep->state = ST_PCB_WAITCALL;
265         }
266 }
267
268 /*---------------------------------------------------------------------------*
269  *      local requested outgoing dial
270  *---------------------------------------------------------------------------*/ 
271 static void
272 F_ACBW(cfg_entry_t *cep)
273 {
274         DBGL(DL_STATE, (log(LL_DBG, "F_ACBW: local callback, wait callback recovery time")));
275
276         if(cep->dialrandincr)
277                 cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
278
279         cep->dial_count = 0;
280
281         cep->cdid = CDID_RESERVED;
282 }
283
284 /*---------------------------------------------------------------------------*
285  *      active callback dialout retry (ST_SUSE !!!)
286  *---------------------------------------------------------------------------*/ 
287 static void
288 F_ACBR(cfg_entry_t *cep)
289 {       
290         cep->dial_count++;
291
292         if(cep->dial_count < cep->dialretries || cep->dialretries == -1) /* Added by FST <mailto:fsteevie@dds.nl> for unlimited dialing (sorry, but I needed it) */
293         {
294                 /* inside normal retry cycle */
295         
296                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial fail, dial retry")));
297                 select_next_dialno(cep);
298                 cep->cdid = CDID_RESERVED;
299                 cep->state = ST_ACB_DIALFAIL;
300                 return;
301         }
302
303         /* retries exhausted */
304         
305         if(!cep->usedown)
306         {
307                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry fail, dial retries exhausted")));
308                 dialresponse(cep, DSTAT_TFAIL);
309                 cep->cdid = CDID_UNUSED;
310                 cep->dial_count = 0;
311                 cep->state = ST_IDLE;
312                 return;
313         }
314
315         /* interface up/down active */
316
317         cep->down_retry_count++;
318
319         if(cep->down_retry_count > cep->downtries)
320         {
321                 /* set interface down */
322                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, setting interface down!")));
323                 dialresponse(cep, DSTAT_PFAIL);
324                 if_down(cep);
325                 cep->state = ST_DOWN;
326         }
327         else
328         {
329                 /* enter new dial retry cycle */
330                 DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, enter new retry cycle!")));
331                 select_next_dialno(cep);
332                 cep->state = ST_ACB_DIALFAIL;
333         }
334
335         cep->dial_count = 0;
336         cep->cdid = CDID_RESERVED;      
337 }
338
339 /*---------------------------------------------------------------------------*
340  *      local requested to send ALERT message
341  *---------------------------------------------------------------------------*/ 
342 static void
343 F_ALRT(cfg_entry_t *cep)
344 {
345         DBGL(DL_STATE, (log(LL_DBG, "F_ALRT: local send alert request")));
346
347         cep->alert_time = cep->alert;
348         
349         sendm_alert_req(cep);
350 }
351
352 /*---------------------------------------------------------------------------*
353  *      isdn daemon state transition table
354  *---------------------------------------------------------------------------*/ 
355 struct state_tab {
356         void(*func)(cfg_entry_t *cep);          /* function to execute */
357         int newstate;                           /* next state */
358 } state_tab[N_EVENTS][N_STATES] = {
359
360 /* 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              */
361 /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
362 /* messages */
363 /* 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}},
364 /* 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}},
365 /* 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}},
366 /* 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}},
367
368 /* local requests */
369 /* 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}},
370 /* 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}},
371 /* 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}},
372 /* 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}},
373
374 /* illegal  */
375
376 /* 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}}
377 };
378
379 /*---------------------------------------------------------------------------*
380  *      event handler
381  *---------------------------------------------------------------------------*/ 
382 void
383 next_state(cfg_entry_t *cep, int event)
384 {
385         int currstate, newstate;
386
387         if(event > N_EVENTS)
388         {
389                 log(LL_ERR, "next_state: event > N_EVENTS");
390                 error_exit(1, "next_state: event > N_EVENTS");
391         }
392
393         currstate = cep->state;
394
395         if(currstate > N_STATES)
396         {
397                 log(LL_ERR, "next_state: currstate > N_STATES");
398                 error_exit(1, "next_state: currstate > N_STATES");
399         }
400
401         newstate = state_tab[event][currstate].newstate;
402
403         if(newstate > N_STATES)
404         {
405                 log(LL_ERR, "next_state: newstate > N_STATES");
406                 error_exit(1, "next_state: newstate > N_STATES");
407         }
408
409         if(newstate != ST_SUSE)
410         {
411                 DBGL(DL_STATE, (log(LL_DBG, "FSM event [%s]: [%s => %s]", event_text[event],
412                                 state_text[currstate],
413                                 state_text[newstate])));
414         }
415
416         (*state_tab[event][currstate].func)(cep);
417
418         if(newstate == ST_ILL)
419         {
420                 log(LL_ERR, "FSM ILLEGAL STATE, event=%s: oldstate=%s => newstate=%s]",
421                                 event_text[event],
422                                 state_text[currstate],
423                                 state_text[newstate]);
424         }
425
426         if(newstate == ST_SUSE)
427         {
428                 DBGL(DL_STATE, (log(LL_DBG, "FSM (SUSE) event [%s]: [%s => %s]", event_text[event],
429                                 state_text[currstate],
430                                 state_text[cep->state])));
431         }
432         else
433         {
434                 cep->state = newstate;
435         }
436 }
437
438 /*---------------------------------------------------------------------------*
439  *      return pointer to current state description
440  *---------------------------------------------------------------------------*/ 
441 char *
442 printstate(cfg_entry_t *cep)
443 {
444         return((char *) state_text[cep->state]);
445 }
446
447 /* EOF */