Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: isdnl3.c,v 2.22.2.3 2004/01/13 14:31:25 keil Exp $ |
2 | * | |
3 | * Author Karsten Keil | |
4 | * based on the teles driver from Jan den Ouden | |
5 | * Copyright by Karsten Keil <keil@isdn4linux.de> | |
475be4d8 | 6 | * |
1da177e4 LT |
7 | * This software may be used and distributed according to the terms |
8 | * of the GNU General Public License, incorporated herein by reference. | |
9 | * | |
10 | * For changes and modifications please read | |
11 | * Documentation/isdn/HiSax.cert | |
12 | * | |
13 | * Thanks to Jan den Ouden | |
14 | * Fritz Elfert | |
15 | * | |
16 | */ | |
17 | ||
18 | #include <linux/init.h> | |
5a0e3ad6 | 19 | #include <linux/slab.h> |
1da177e4 LT |
20 | #include "hisax.h" |
21 | #include "isdnl3.h" | |
1da177e4 LT |
22 | |
23 | const char *l3_revision = "$Revision: 2.22.2.3 $"; | |
24 | ||
25 | static struct Fsm l3fsm; | |
26 | ||
27 | enum { | |
28 | ST_L3_LC_REL, | |
29 | ST_L3_LC_ESTAB_WAIT, | |
475be4d8 | 30 | ST_L3_LC_REL_DELAY, |
1da177e4 LT |
31 | ST_L3_LC_REL_WAIT, |
32 | ST_L3_LC_ESTAB, | |
33 | }; | |
34 | ||
475be4d8 | 35 | #define L3_STATE_COUNT (ST_L3_LC_ESTAB + 1) |
1da177e4 LT |
36 | |
37 | static char *strL3State[] = | |
38 | { | |
39 | "ST_L3_LC_REL", | |
40 | "ST_L3_LC_ESTAB_WAIT", | |
41 | "ST_L3_LC_REL_DELAY", | |
42 | "ST_L3_LC_REL_WAIT", | |
43 | "ST_L3_LC_ESTAB", | |
44 | }; | |
45 | ||
46 | enum { | |
47 | EV_ESTABLISH_REQ, | |
48 | EV_ESTABLISH_IND, | |
49 | EV_ESTABLISH_CNF, | |
50 | EV_RELEASE_REQ, | |
51 | EV_RELEASE_CNF, | |
52 | EV_RELEASE_IND, | |
53 | EV_TIMEOUT, | |
54 | }; | |
55 | ||
475be4d8 | 56 | #define L3_EVENT_COUNT (EV_TIMEOUT + 1) |
1da177e4 LT |
57 | |
58 | static char *strL3Event[] = | |
59 | { | |
60 | "EV_ESTABLISH_REQ", | |
61 | "EV_ESTABLISH_IND", | |
62 | "EV_ESTABLISH_CNF", | |
63 | "EV_RELEASE_REQ", | |
64 | "EV_RELEASE_CNF", | |
65 | "EV_RELEASE_IND", | |
66 | "EV_TIMEOUT", | |
67 | }; | |
68 | ||
b9075fa9 | 69 | static __printf(2, 3) void |
475be4d8 | 70 | l3m_debug(struct FsmInst *fi, char *fmt, ...) |
1da177e4 LT |
71 | { |
72 | va_list args; | |
73 | struct PStack *st = fi->userdata; | |
74 | ||
75 | va_start(args, fmt); | |
76 | VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args); | |
77 | va_end(args); | |
78 | } | |
79 | ||
80 | u_char * | |
475be4d8 | 81 | findie(u_char *p, int size, u_char ie, int wanted_set) |
1da177e4 LT |
82 | { |
83 | int l, codeset, maincodeset; | |
84 | u_char *pend = p + size; | |
85 | ||
86 | /* skip protocol discriminator, callref and message type */ | |
87 | p++; | |
88 | l = (*p++) & 0xf; | |
89 | p += l; | |
90 | p++; | |
91 | codeset = 0; | |
92 | maincodeset = 0; | |
93 | /* while there are bytes left... */ | |
94 | while (p < pend) { | |
95 | if ((*p & 0xf0) == 0x90) { | |
96 | codeset = *p & 0x07; | |
97 | if (!(*p & 0x08)) | |
98 | maincodeset = codeset; | |
99 | } | |
100 | if (*p & 0x80) | |
101 | p++; | |
102 | else { | |
103 | if (codeset == wanted_set) { | |
104 | if (*p == ie) | |
475be4d8 JP |
105 | { /* improved length check (Werner Cornelius) */ |
106 | if ((pend - p) < 2) | |
107 | return (NULL); | |
108 | if (*(p + 1) > (pend - (p + 2))) | |
109 | return (NULL); | |
110 | return (p); | |
111 | } | |
112 | ||
1da177e4 LT |
113 | if (*p > ie) |
114 | return (NULL); | |
115 | } | |
116 | p++; | |
117 | l = *p++; | |
118 | p += l; | |
119 | codeset = maincodeset; | |
120 | } | |
121 | } | |
122 | return (NULL); | |
123 | } | |
124 | ||
125 | int | |
475be4d8 | 126 | getcallref(u_char *p) |
1da177e4 LT |
127 | { |
128 | int l, cr = 0; | |
129 | ||
130 | p++; /* prot discr */ | |
131 | if (*p & 0xfe) /* wrong callref BRI only 1 octet*/ | |
475be4d8 | 132 | return (-2); |
1da177e4 LT |
133 | l = 0xf & *p++; /* callref length */ |
134 | if (!l) /* dummy CallRef */ | |
475be4d8 | 135 | return (-1); |
1da177e4 LT |
136 | cr = *p++; |
137 | return (cr); | |
138 | } | |
139 | ||
140 | static int OrigCallRef = 0; | |
141 | ||
142 | int | |
143 | newcallref(void) | |
144 | { | |
145 | if (OrigCallRef == 127) | |
146 | OrigCallRef = 1; | |
147 | else | |
148 | OrigCallRef++; | |
149 | return (OrigCallRef); | |
150 | } | |
151 | ||
152 | void | |
153 | newl3state(struct l3_process *pc, int state) | |
154 | { | |
155 | if (pc->debug & L3_DEB_STATE) | |
475be4d8 | 156 | l3_debug(pc->st, "newstate cr %d %d --> %d", |
1da177e4 LT |
157 | pc->callref & 0x7F, |
158 | pc->state, state); | |
159 | pc->state = state; | |
160 | } | |
161 | ||
162 | static void | |
163 | L3ExpireTimer(struct L3Timer *t) | |
164 | { | |
165 | t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc); | |
166 | } | |
167 | ||
168 | void | |
169 | L3InitTimer(struct l3_process *pc, struct L3Timer *t) | |
170 | { | |
171 | t->pc = pc; | |
172 | t->tl.function = (void *) L3ExpireTimer; | |
173 | t->tl.data = (long) t; | |
174 | init_timer(&t->tl); | |
175 | } | |
176 | ||
177 | void | |
178 | L3DelTimer(struct L3Timer *t) | |
179 | { | |
180 | del_timer(&t->tl); | |
181 | } | |
182 | ||
183 | int | |
184 | L3AddTimer(struct L3Timer *t, | |
185 | int millisec, int event) | |
186 | { | |
187 | if (timer_pending(&t->tl)) { | |
188 | printk(KERN_WARNING "L3AddTimer: timer already active!\n"); | |
189 | return -1; | |
190 | } | |
191 | init_timer(&t->tl); | |
192 | t->event = event; | |
193 | t->tl.expires = jiffies + (millisec * HZ) / 1000; | |
194 | add_timer(&t->tl); | |
195 | return 0; | |
196 | } | |
197 | ||
198 | void | |
199 | StopAllL3Timer(struct l3_process *pc) | |
200 | { | |
201 | L3DelTimer(&pc->timer); | |
202 | } | |
203 | ||
204 | struct sk_buff * | |
205 | l3_alloc_skb(int len) | |
206 | { | |
207 | struct sk_buff *skb; | |
208 | ||
209 | if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) { | |
210 | printk(KERN_WARNING "HiSax: No skb for D-channel\n"); | |
211 | return (NULL); | |
212 | } | |
213 | skb_reserve(skb, MAX_HEADER_LEN); | |
214 | return (skb); | |
215 | } | |
216 | ||
217 | static void | |
218 | no_l3_proto(struct PStack *st, int pr, void *arg) | |
219 | { | |
220 | struct sk_buff *skb = arg; | |
221 | ||
222 | HiSax_putstatus(st->l1.hardware, "L3", "no D protocol"); | |
223 | if (skb) { | |
224 | dev_kfree_skb(skb); | |
225 | } | |
226 | } | |
227 | ||
228 | static int | |
229 | no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic) | |
230 | { | |
475be4d8 JP |
231 | printk(KERN_WARNING "HiSax: no specific protocol handler for proto %lu\n", ic->arg & 0xFF); |
232 | return (-1); | |
1da177e4 LT |
233 | } |
234 | ||
1da177e4 LT |
235 | struct l3_process |
236 | *getl3proc(struct PStack *st, int cr) | |
237 | { | |
238 | struct l3_process *p = st->l3.proc; | |
239 | ||
240 | while (p) | |
241 | if (p->callref == cr) | |
242 | return (p); | |
243 | else | |
244 | p = p->next; | |
245 | return (NULL); | |
246 | } | |
247 | ||
248 | struct l3_process | |
249 | *new_l3_process(struct PStack *st, int cr) | |
250 | { | |
251 | struct l3_process *p, *np; | |
252 | ||
253 | if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) { | |
254 | printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr); | |
255 | return (NULL); | |
256 | } | |
257 | if (!st->l3.proc) | |
258 | st->l3.proc = p; | |
259 | else { | |
260 | np = st->l3.proc; | |
261 | while (np->next) | |
262 | np = np->next; | |
263 | np->next = p; | |
264 | } | |
265 | p->next = NULL; | |
266 | p->debug = st->l3.debug; | |
267 | p->callref = cr; | |
268 | p->state = 0; | |
269 | p->chan = NULL; | |
270 | p->st = st; | |
271 | p->N303 = st->l3.N303; | |
272 | L3InitTimer(p, &p->timer); | |
273 | return (p); | |
274 | }; | |
275 | ||
276 | void | |
277 | release_l3_process(struct l3_process *p) | |
278 | { | |
279 | struct l3_process *np, *pp = NULL; | |
280 | ||
281 | if (!p) | |
282 | return; | |
283 | np = p->st->l3.proc; | |
284 | while (np) { | |
285 | if (np == p) { | |
286 | StopAllL3Timer(p); | |
287 | if (pp) | |
288 | pp->next = np->next; | |
289 | else if (!(p->st->l3.proc = np->next) && | |
475be4d8 | 290 | !test_bit(FLG_PTP, &p->st->l2.flag)) { |
1da177e4 LT |
291 | if (p->debug) |
292 | l3_debug(p->st, "release_l3_process: last process"); | |
b03efcfb | 293 | if (skb_queue_empty(&p->st->l3.squeue)) { |
1da177e4 LT |
294 | if (p->debug) |
295 | l3_debug(p->st, "release_l3_process: release link"); | |
296 | if (p->st->protocol != ISDN_PTYPE_NI1) | |
297 | FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL); | |
298 | else | |
299 | FsmEvent(&p->st->l3.l3m, EV_RELEASE_IND, NULL); | |
300 | } else { | |
301 | if (p->debug) | |
302 | l3_debug(p->st, "release_l3_process: not release link"); | |
303 | } | |
475be4d8 | 304 | } |
1da177e4 LT |
305 | kfree(p); |
306 | return; | |
307 | } | |
308 | pp = np; | |
309 | np = np->next; | |
310 | } | |
311 | printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref); | |
312 | l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref); | |
313 | }; | |
314 | ||
315 | static void | |
316 | l3ml3p(struct PStack *st, int pr) | |
317 | { | |
318 | struct l3_process *p = st->l3.proc; | |
319 | struct l3_process *np; | |
320 | ||
321 | while (p) { | |
322 | /* p might be kfreed under us, so we need to save where we want to go on */ | |
323 | np = p->next; | |
324 | st->l3.l3ml3(st, pr, p); | |
325 | p = np; | |
326 | } | |
327 | } | |
328 | ||
329 | void | |
330 | setstack_l3dc(struct PStack *st, struct Channel *chanp) | |
331 | { | |
332 | char tmp[64]; | |
333 | ||
334 | st->l3.proc = NULL; | |
335 | st->l3.global = NULL; | |
336 | skb_queue_head_init(&st->l3.squeue); | |
337 | st->l3.l3m.fsm = &l3fsm; | |
338 | st->l3.l3m.state = ST_L3_LC_REL; | |
339 | st->l3.l3m.debug = 1; | |
340 | st->l3.l3m.userdata = st; | |
341 | st->l3.l3m.userint = 0; | |
342 | st->l3.l3m.printdebug = l3m_debug; | |
475be4d8 | 343 | FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer); |
1da177e4 LT |
344 | strcpy(st->l3.debug_id, "L3DC "); |
345 | st->lli.l4l3_proto = no_l3_proto_spec; | |
346 | ||
475be4d8 | 347 | #ifdef CONFIG_HISAX_EURO |
1da177e4 LT |
348 | if (st->protocol == ISDN_PTYPE_EURO) { |
349 | setstack_dss1(st); | |
350 | } else | |
351 | #endif | |
475be4d8 JP |
352 | #ifdef CONFIG_HISAX_NI1 |
353 | if (st->protocol == ISDN_PTYPE_NI1) { | |
354 | setstack_ni1(st); | |
355 | } else | |
1da177e4 | 356 | #endif |
475be4d8 JP |
357 | #ifdef CONFIG_HISAX_1TR6 |
358 | if (st->protocol == ISDN_PTYPE_1TR6) { | |
359 | setstack_1tr6(st); | |
360 | } else | |
1da177e4 | 361 | #endif |
475be4d8 JP |
362 | if (st->protocol == ISDN_PTYPE_LEASED) { |
363 | st->lli.l4l3 = no_l3_proto; | |
364 | st->l2.l2l3 = no_l3_proto; | |
365 | st->l3.l3ml3 = no_l3_proto; | |
366 | printk(KERN_INFO "HiSax: Leased line mode\n"); | |
367 | } else { | |
368 | st->lli.l4l3 = no_l3_proto; | |
369 | st->l2.l2l3 = no_l3_proto; | |
370 | st->l3.l3ml3 = no_l3_proto; | |
371 | sprintf(tmp, "protocol %s not supported", | |
372 | (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" : | |
373 | (st->protocol == ISDN_PTYPE_EURO) ? "euro" : | |
374 | (st->protocol == ISDN_PTYPE_NI1) ? "ni1" : | |
375 | "unknown"); | |
376 | printk(KERN_WARNING "HiSax: %s\n", tmp); | |
377 | st->protocol = -1; | |
378 | } | |
1da177e4 LT |
379 | } |
380 | ||
672c3fd9 | 381 | static void |
1da177e4 LT |
382 | isdnl3_trans(struct PStack *st, int pr, void *arg) { |
383 | st->l3.l3l2(st, pr, arg); | |
384 | } | |
385 | ||
386 | void | |
387 | releasestack_isdnl3(struct PStack *st) | |
388 | { | |
389 | while (st->l3.proc) | |
390 | release_l3_process(st->l3.proc); | |
391 | if (st->l3.global) { | |
392 | StopAllL3Timer(st->l3.global); | |
393 | kfree(st->l3.global); | |
394 | st->l3.global = NULL; | |
395 | } | |
396 | FsmDelTimer(&st->l3.l3m_timer, 54); | |
397 | skb_queue_purge(&st->l3.squeue); | |
398 | } | |
399 | ||
400 | void | |
401 | setstack_l3bc(struct PStack *st, struct Channel *chanp) | |
402 | { | |
403 | ||
404 | st->l3.proc = NULL; | |
405 | st->l3.global = NULL; | |
406 | skb_queue_head_init(&st->l3.squeue); | |
407 | st->l3.l3m.fsm = &l3fsm; | |
408 | st->l3.l3m.state = ST_L3_LC_REL; | |
409 | st->l3.l3m.debug = 1; | |
410 | st->l3.l3m.userdata = st; | |
411 | st->l3.l3m.userint = 0; | |
412 | st->l3.l3m.printdebug = l3m_debug; | |
413 | strcpy(st->l3.debug_id, "L3BC "); | |
414 | st->lli.l4l3 = isdnl3_trans; | |
415 | } | |
416 | ||
417 | #define DREL_TIMER_VALUE 40000 | |
418 | ||
419 | static void | |
420 | lc_activate(struct FsmInst *fi, int event, void *arg) | |
421 | { | |
422 | struct PStack *st = fi->userdata; | |
423 | ||
424 | FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT); | |
425 | st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL); | |
426 | } | |
427 | ||
428 | static void | |
429 | lc_connect(struct FsmInst *fi, int event, void *arg) | |
430 | { | |
431 | struct PStack *st = fi->userdata; | |
432 | struct sk_buff *skb = arg; | |
433 | int dequeued = 0; | |
434 | ||
435 | FsmChangeState(fi, ST_L3_LC_ESTAB); | |
436 | while ((skb = skb_dequeue(&st->l3.squeue))) { | |
437 | st->l3.l3l2(st, DL_DATA | REQUEST, skb); | |
438 | dequeued++; | |
439 | } | |
440 | if ((!st->l3.proc) && dequeued) { | |
441 | if (st->l3.debug) | |
442 | l3_debug(st, "lc_connect: release link"); | |
443 | FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); | |
444 | } else | |
445 | l3ml3p(st, DL_ESTABLISH | INDICATION); | |
446 | } | |
447 | ||
448 | static void | |
449 | lc_connected(struct FsmInst *fi, int event, void *arg) | |
450 | { | |
451 | struct PStack *st = fi->userdata; | |
452 | struct sk_buff *skb = arg; | |
453 | int dequeued = 0; | |
454 | ||
455 | FsmDelTimer(&st->l3.l3m_timer, 51); | |
456 | FsmChangeState(fi, ST_L3_LC_ESTAB); | |
457 | while ((skb = skb_dequeue(&st->l3.squeue))) { | |
458 | st->l3.l3l2(st, DL_DATA | REQUEST, skb); | |
459 | dequeued++; | |
460 | } | |
461 | if ((!st->l3.proc) && dequeued) { | |
462 | if (st->l3.debug) | |
463 | l3_debug(st, "lc_connected: release link"); | |
464 | FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); | |
465 | } else | |
466 | l3ml3p(st, DL_ESTABLISH | CONFIRM); | |
467 | } | |
468 | ||
469 | static void | |
470 | lc_start_delay(struct FsmInst *fi, int event, void *arg) | |
471 | { | |
475be4d8 | 472 | struct PStack *st = fi->userdata; |
1da177e4 | 473 | |
475be4d8 JP |
474 | FsmChangeState(fi, ST_L3_LC_REL_DELAY); |
475 | FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); | |
1da177e4 LT |
476 | } |
477 | ||
478 | static void | |
479 | lc_start_delay_check(struct FsmInst *fi, int event, void *arg) | |
480 | /* 20/09/00 - GE timer not user for NI-1 as layer 2 should stay up */ | |
481 | { | |
475be4d8 | 482 | struct PStack *st = fi->userdata; |
1da177e4 | 483 | |
475be4d8 JP |
484 | FsmChangeState(fi, ST_L3_LC_REL_DELAY); |
485 | /* 19/09/00 - GE timer not user for NI-1 */ | |
486 | if (st->protocol != ISDN_PTYPE_NI1) | |
487 | FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50); | |
1da177e4 LT |
488 | } |
489 | ||
490 | static void | |
491 | lc_release_req(struct FsmInst *fi, int event, void *arg) | |
492 | { | |
493 | struct PStack *st = fi->userdata; | |
494 | ||
495 | if (test_bit(FLG_L2BLOCK, &st->l2.flag)) { | |
496 | if (st->l3.debug) | |
497 | l3_debug(st, "lc_release_req: l2 blocked"); | |
498 | /* restart release timer */ | |
499 | FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51); | |
500 | } else { | |
501 | FsmChangeState(fi, ST_L3_LC_REL_WAIT); | |
502 | st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL); | |
503 | } | |
504 | } | |
505 | ||
506 | static void | |
507 | lc_release_ind(struct FsmInst *fi, int event, void *arg) | |
508 | { | |
509 | struct PStack *st = fi->userdata; | |
510 | ||
511 | FsmDelTimer(&st->l3.l3m_timer, 52); | |
512 | FsmChangeState(fi, ST_L3_LC_REL); | |
513 | skb_queue_purge(&st->l3.squeue); | |
514 | l3ml3p(st, DL_RELEASE | INDICATION); | |
515 | } | |
516 | ||
517 | static void | |
518 | lc_release_cnf(struct FsmInst *fi, int event, void *arg) | |
519 | { | |
520 | struct PStack *st = fi->userdata; | |
521 | ||
522 | FsmChangeState(fi, ST_L3_LC_REL); | |
523 | skb_queue_purge(&st->l3.squeue); | |
524 | l3ml3p(st, DL_RELEASE | CONFIRM); | |
525 | } | |
526 | ||
527 | ||
528 | /* *INDENT-OFF* */ | |
529 | static struct FsmNode L3FnList[] __initdata = | |
530 | { | |
531 | {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate}, | |
532 | {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect}, | |
533 | {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect}, | |
534 | {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connected}, | |
535 | {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay}, | |
536 | {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind}, | |
537 | {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind}, | |
538 | {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_start_delay_check}, | |
475be4d8 JP |
539 | {ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind}, |
540 | {ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected}, | |
541 | {ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req}, | |
1da177e4 LT |
542 | {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf}, |
543 | {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate}, | |
544 | }; | |
545 | /* *INDENT-ON* */ | |
546 | ||
1da177e4 LT |
547 | void |
548 | l3_msg(struct PStack *st, int pr, void *arg) | |
549 | { | |
550 | switch (pr) { | |
475be4d8 JP |
551 | case (DL_DATA | REQUEST): |
552 | if (st->l3.l3m.state == ST_L3_LC_ESTAB) { | |
553 | st->l3.l3l2(st, pr, arg); | |
554 | } else { | |
555 | struct sk_buff *skb = arg; | |
556 | ||
557 | skb_queue_tail(&st->l3.squeue, skb); | |
1da177e4 | 558 | FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); |
475be4d8 JP |
559 | } |
560 | break; | |
561 | case (DL_ESTABLISH | REQUEST): | |
562 | FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL); | |
563 | break; | |
564 | case (DL_ESTABLISH | CONFIRM): | |
565 | FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL); | |
566 | break; | |
567 | case (DL_ESTABLISH | INDICATION): | |
568 | FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL); | |
569 | break; | |
570 | case (DL_RELEASE | INDICATION): | |
571 | FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL); | |
572 | break; | |
573 | case (DL_RELEASE | CONFIRM): | |
574 | FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL); | |
575 | break; | |
576 | case (DL_RELEASE | REQUEST): | |
577 | FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL); | |
578 | break; | |
1da177e4 LT |
579 | } |
580 | } | |
581 | ||
582 | int __init | |
583 | Isdnl3New(void) | |
584 | { | |
585 | l3fsm.state_count = L3_STATE_COUNT; | |
586 | l3fsm.event_count = L3_EVENT_COUNT; | |
587 | l3fsm.strEvent = strL3Event; | |
588 | l3fsm.strState = strL3State; | |
ba2d6ccb | 589 | return FsmNew(&l3fsm, L3FnList, ARRAY_SIZE(L3FnList)); |
1da177e4 LT |
590 | } |
591 | ||
592 | void | |
593 | Isdnl3Free(void) | |
594 | { | |
595 | FsmFree(&l3fsm); | |
596 | } |