diff options
Diffstat (limited to 'drivers/isdn/mISDN/layer1.c')
-rw-r--r-- | drivers/isdn/mISDN/layer1.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c new file mode 100644 index 000000000000..fced1a2755f8 --- /dev/null +++ b/drivers/isdn/mISDN/layer1.c | |||
@@ -0,0 +1,403 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Author Karsten Keil <kkeil@novell.com> | ||
4 | * | ||
5 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/mISDNhw.h> | ||
21 | #include "layer1.h" | ||
22 | #include "fsm.h" | ||
23 | |||
24 | static int *debug; | ||
25 | |||
26 | struct layer1 { | ||
27 | u_long Flags; | ||
28 | struct FsmInst l1m; | ||
29 | struct FsmTimer timer; | ||
30 | int delay; | ||
31 | struct dchannel *dch; | ||
32 | dchannel_l1callback *dcb; | ||
33 | }; | ||
34 | |||
35 | #define TIMER3_VALUE 7000 | ||
36 | |||
37 | static | ||
38 | struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; | ||
39 | |||
40 | enum { | ||
41 | ST_L1_F2, | ||
42 | ST_L1_F3, | ||
43 | ST_L1_F4, | ||
44 | ST_L1_F5, | ||
45 | ST_L1_F6, | ||
46 | ST_L1_F7, | ||
47 | ST_L1_F8, | ||
48 | }; | ||
49 | |||
50 | #define L1S_STATE_COUNT (ST_L1_F8+1) | ||
51 | |||
52 | static char *strL1SState[] = | ||
53 | { | ||
54 | "ST_L1_F2", | ||
55 | "ST_L1_F3", | ||
56 | "ST_L1_F4", | ||
57 | "ST_L1_F5", | ||
58 | "ST_L1_F6", | ||
59 | "ST_L1_F7", | ||
60 | "ST_L1_F8", | ||
61 | }; | ||
62 | |||
63 | enum { | ||
64 | EV_PH_ACTIVATE, | ||
65 | EV_PH_DEACTIVATE, | ||
66 | EV_RESET_IND, | ||
67 | EV_DEACT_CNF, | ||
68 | EV_DEACT_IND, | ||
69 | EV_POWER_UP, | ||
70 | EV_ANYSIG_IND, | ||
71 | EV_INFO2_IND, | ||
72 | EV_INFO4_IND, | ||
73 | EV_TIMER_DEACT, | ||
74 | EV_TIMER_ACT, | ||
75 | EV_TIMER3, | ||
76 | }; | ||
77 | |||
78 | #define L1_EVENT_COUNT (EV_TIMER3 + 1) | ||
79 | |||
80 | static char *strL1Event[] = | ||
81 | { | ||
82 | "EV_PH_ACTIVATE", | ||
83 | "EV_PH_DEACTIVATE", | ||
84 | "EV_RESET_IND", | ||
85 | "EV_DEACT_CNF", | ||
86 | "EV_DEACT_IND", | ||
87 | "EV_POWER_UP", | ||
88 | "EV_ANYSIG_IND", | ||
89 | "EV_INFO2_IND", | ||
90 | "EV_INFO4_IND", | ||
91 | "EV_TIMER_DEACT", | ||
92 | "EV_TIMER_ACT", | ||
93 | "EV_TIMER3", | ||
94 | }; | ||
95 | |||
96 | static void | ||
97 | l1m_debug(struct FsmInst *fi, char *fmt, ...) | ||
98 | { | ||
99 | struct layer1 *l1 = fi->userdata; | ||
100 | va_list va; | ||
101 | |||
102 | va_start(va, fmt); | ||
103 | printk(KERN_DEBUG "%s: ", l1->dch->dev.name); | ||
104 | vprintk(fmt, va); | ||
105 | printk("\n"); | ||
106 | va_end(va); | ||
107 | } | ||
108 | |||
109 | static void | ||
110 | l1_reset(struct FsmInst *fi, int event, void *arg) | ||
111 | { | ||
112 | mISDN_FsmChangeState(fi, ST_L1_F3); | ||
113 | } | ||
114 | |||
115 | static void | ||
116 | l1_deact_cnf(struct FsmInst *fi, int event, void *arg) | ||
117 | { | ||
118 | struct layer1 *l1 = fi->userdata; | ||
119 | |||
120 | mISDN_FsmChangeState(fi, ST_L1_F3); | ||
121 | if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) | ||
122 | l1->dcb(l1->dch, HW_POWERUP_REQ); | ||
123 | } | ||
124 | |||
125 | static void | ||
126 | l1_deact_req_s(struct FsmInst *fi, int event, void *arg) | ||
127 | { | ||
128 | struct layer1 *l1 = fi->userdata; | ||
129 | |||
130 | mISDN_FsmChangeState(fi, ST_L1_F3); | ||
131 | mISDN_FsmRestartTimer(&l1->timer, 550, EV_TIMER_DEACT, NULL, 2); | ||
132 | test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags); | ||
133 | } | ||
134 | |||
135 | static void | ||
136 | l1_power_up_s(struct FsmInst *fi, int event, void *arg) | ||
137 | { | ||
138 | struct layer1 *l1 = fi->userdata; | ||
139 | |||
140 | if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { | ||
141 | mISDN_FsmChangeState(fi, ST_L1_F4); | ||
142 | l1->dcb(l1->dch, INFO3_P8); | ||
143 | } else | ||
144 | mISDN_FsmChangeState(fi, ST_L1_F3); | ||
145 | } | ||
146 | |||
147 | static void | ||
148 | l1_go_F5(struct FsmInst *fi, int event, void *arg) | ||
149 | { | ||
150 | mISDN_FsmChangeState(fi, ST_L1_F5); | ||
151 | } | ||
152 | |||
153 | static void | ||
154 | l1_go_F8(struct FsmInst *fi, int event, void *arg) | ||
155 | { | ||
156 | mISDN_FsmChangeState(fi, ST_L1_F8); | ||
157 | } | ||
158 | |||
159 | static void | ||
160 | l1_info2_ind(struct FsmInst *fi, int event, void *arg) | ||
161 | { | ||
162 | struct layer1 *l1 = fi->userdata; | ||
163 | |||
164 | mISDN_FsmChangeState(fi, ST_L1_F6); | ||
165 | l1->dcb(l1->dch, INFO3_P8); | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | l1_info4_ind(struct FsmInst *fi, int event, void *arg) | ||
170 | { | ||
171 | struct layer1 *l1 = fi->userdata; | ||
172 | |||
173 | mISDN_FsmChangeState(fi, ST_L1_F7); | ||
174 | l1->dcb(l1->dch, INFO3_P8); | ||
175 | if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags)) | ||
176 | mISDN_FsmDelTimer(&l1->timer, 4); | ||
177 | if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { | ||
178 | if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags)) | ||
179 | mISDN_FsmDelTimer(&l1->timer, 3); | ||
180 | mISDN_FsmRestartTimer(&l1->timer, 110, EV_TIMER_ACT, NULL, 2); | ||
181 | test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | static void | ||
186 | l1_timer3(struct FsmInst *fi, int event, void *arg) | ||
187 | { | ||
188 | struct layer1 *l1 = fi->userdata; | ||
189 | |||
190 | test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags); | ||
191 | if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) { | ||
192 | if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) | ||
193 | l1->dcb(l1->dch, HW_D_NOBLOCKED); | ||
194 | l1->dcb(l1->dch, PH_DEACTIVATE_IND); | ||
195 | } | ||
196 | if (l1->l1m.state != ST_L1_F6) { | ||
197 | mISDN_FsmChangeState(fi, ST_L1_F3); | ||
198 | l1->dcb(l1->dch, HW_POWERUP_REQ); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | static void | ||
203 | l1_timer_act(struct FsmInst *fi, int event, void *arg) | ||
204 | { | ||
205 | struct layer1 *l1 = fi->userdata; | ||
206 | |||
207 | test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags); | ||
208 | test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags); | ||
209 | l1->dcb(l1->dch, PH_ACTIVATE_IND); | ||
210 | } | ||
211 | |||
212 | static void | ||
213 | l1_timer_deact(struct FsmInst *fi, int event, void *arg) | ||
214 | { | ||
215 | struct layer1 *l1 = fi->userdata; | ||
216 | |||
217 | test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags); | ||
218 | test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags); | ||
219 | if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) | ||
220 | l1->dcb(l1->dch, HW_D_NOBLOCKED); | ||
221 | l1->dcb(l1->dch, PH_DEACTIVATE_IND); | ||
222 | l1->dcb(l1->dch, HW_DEACT_REQ); | ||
223 | } | ||
224 | |||
225 | static void | ||
226 | l1_activate_s(struct FsmInst *fi, int event, void *arg) | ||
227 | { | ||
228 | struct layer1 *l1 = fi->userdata; | ||
229 | |||
230 | mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); | ||
231 | test_and_set_bit(FLG_L1_T3RUN, &l1->Flags); | ||
232 | l1->dcb(l1->dch, HW_RESET_REQ); | ||
233 | } | ||
234 | |||
235 | static void | ||
236 | l1_activate_no(struct FsmInst *fi, int event, void *arg) | ||
237 | { | ||
238 | struct layer1 *l1 = fi->userdata; | ||
239 | |||
240 | if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) && | ||
241 | (!test_bit(FLG_L1_T3RUN, &l1->Flags))) { | ||
242 | test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags); | ||
243 | if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags)) | ||
244 | l1->dcb(l1->dch, HW_D_NOBLOCKED); | ||
245 | l1->dcb(l1->dch, PH_DEACTIVATE_IND); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static struct FsmNode L1SFnList[] = | ||
250 | { | ||
251 | {ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s}, | ||
252 | {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no}, | ||
253 | {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no}, | ||
254 | {ST_L1_F3, EV_RESET_IND, l1_reset}, | ||
255 | {ST_L1_F4, EV_RESET_IND, l1_reset}, | ||
256 | {ST_L1_F5, EV_RESET_IND, l1_reset}, | ||
257 | {ST_L1_F6, EV_RESET_IND, l1_reset}, | ||
258 | {ST_L1_F7, EV_RESET_IND, l1_reset}, | ||
259 | {ST_L1_F8, EV_RESET_IND, l1_reset}, | ||
260 | {ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf}, | ||
261 | {ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf}, | ||
262 | {ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf}, | ||
263 | {ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf}, | ||
264 | {ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf}, | ||
265 | {ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf}, | ||
266 | {ST_L1_F6, EV_DEACT_IND, l1_deact_req_s}, | ||
267 | {ST_L1_F7, EV_DEACT_IND, l1_deact_req_s}, | ||
268 | {ST_L1_F8, EV_DEACT_IND, l1_deact_req_s}, | ||
269 | {ST_L1_F3, EV_POWER_UP, l1_power_up_s}, | ||
270 | {ST_L1_F4, EV_ANYSIG_IND, l1_go_F5}, | ||
271 | {ST_L1_F6, EV_ANYSIG_IND, l1_go_F8}, | ||
272 | {ST_L1_F7, EV_ANYSIG_IND, l1_go_F8}, | ||
273 | {ST_L1_F3, EV_INFO2_IND, l1_info2_ind}, | ||
274 | {ST_L1_F4, EV_INFO2_IND, l1_info2_ind}, | ||
275 | {ST_L1_F5, EV_INFO2_IND, l1_info2_ind}, | ||
276 | {ST_L1_F7, EV_INFO2_IND, l1_info2_ind}, | ||
277 | {ST_L1_F8, EV_INFO2_IND, l1_info2_ind}, | ||
278 | {ST_L1_F3, EV_INFO4_IND, l1_info4_ind}, | ||
279 | {ST_L1_F4, EV_INFO4_IND, l1_info4_ind}, | ||
280 | {ST_L1_F5, EV_INFO4_IND, l1_info4_ind}, | ||
281 | {ST_L1_F6, EV_INFO4_IND, l1_info4_ind}, | ||
282 | {ST_L1_F8, EV_INFO4_IND, l1_info4_ind}, | ||
283 | {ST_L1_F3, EV_TIMER3, l1_timer3}, | ||
284 | {ST_L1_F4, EV_TIMER3, l1_timer3}, | ||
285 | {ST_L1_F5, EV_TIMER3, l1_timer3}, | ||
286 | {ST_L1_F6, EV_TIMER3, l1_timer3}, | ||
287 | {ST_L1_F8, EV_TIMER3, l1_timer3}, | ||
288 | {ST_L1_F7, EV_TIMER_ACT, l1_timer_act}, | ||
289 | {ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact}, | ||
290 | {ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact}, | ||
291 | {ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact}, | ||
292 | {ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact}, | ||
293 | {ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact}, | ||
294 | {ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact}, | ||
295 | }; | ||
296 | |||
297 | static void | ||
298 | release_l1(struct layer1 *l1) { | ||
299 | mISDN_FsmDelTimer(&l1->timer, 0); | ||
300 | if (l1->dch) | ||
301 | l1->dch->l1 = NULL; | ||
302 | module_put(THIS_MODULE); | ||
303 | kfree(l1); | ||
304 | } | ||
305 | |||
306 | int | ||
307 | l1_event(struct layer1 *l1, u_int event) | ||
308 | { | ||
309 | int err = 0; | ||
310 | |||
311 | if (!l1) | ||
312 | return -EINVAL; | ||
313 | switch (event) { | ||
314 | case HW_RESET_IND: | ||
315 | mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); | ||
316 | break; | ||
317 | case HW_DEACT_IND: | ||
318 | mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); | ||
319 | break; | ||
320 | case HW_POWERUP_IND: | ||
321 | mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); | ||
322 | break; | ||
323 | case HW_DEACT_CNF: | ||
324 | mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); | ||
325 | break; | ||
326 | case ANYSIGNAL: | ||
327 | mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); | ||
328 | break; | ||
329 | case LOSTFRAMING: | ||
330 | mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); | ||
331 | break; | ||
332 | case INFO2: | ||
333 | mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); | ||
334 | break; | ||
335 | case INFO4_P8: | ||
336 | mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); | ||
337 | break; | ||
338 | case INFO4_P10: | ||
339 | mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); | ||
340 | break; | ||
341 | case PH_ACTIVATE_REQ: | ||
342 | if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) | ||
343 | l1->dcb(l1->dch, PH_ACTIVATE_IND); | ||
344 | else { | ||
345 | test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags); | ||
346 | mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); | ||
347 | } | ||
348 | break; | ||
349 | case CLOSE_CHANNEL: | ||
350 | release_l1(l1); | ||
351 | break; | ||
352 | default: | ||
353 | if (*debug & DEBUG_L1) | ||
354 | printk(KERN_DEBUG "%s %x unhandled\n", | ||
355 | __func__, event); | ||
356 | err = -EINVAL; | ||
357 | } | ||
358 | return err; | ||
359 | } | ||
360 | EXPORT_SYMBOL(l1_event); | ||
361 | |||
362 | int | ||
363 | create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { | ||
364 | struct layer1 *nl1; | ||
365 | |||
366 | nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC); | ||
367 | if (!nl1) { | ||
368 | printk(KERN_ERR "kmalloc struct layer1 failed\n"); | ||
369 | return -ENOMEM; | ||
370 | } | ||
371 | nl1->l1m.fsm = &l1fsm_s; | ||
372 | nl1->l1m.state = ST_L1_F3; | ||
373 | nl1->Flags = 0; | ||
374 | nl1->l1m.debug = *debug & DEBUG_L1_FSM; | ||
375 | nl1->l1m.userdata = nl1; | ||
376 | nl1->l1m.userint = 0; | ||
377 | nl1->l1m.printdebug = l1m_debug; | ||
378 | nl1->dch = dch; | ||
379 | nl1->dcb = dcb; | ||
380 | mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer); | ||
381 | __module_get(THIS_MODULE); | ||
382 | dch->l1 = nl1; | ||
383 | return 0; | ||
384 | } | ||
385 | EXPORT_SYMBOL(create_l1); | ||
386 | |||
387 | int | ||
388 | l1_init(u_int *deb) | ||
389 | { | ||
390 | debug = deb; | ||
391 | l1fsm_s.state_count = L1S_STATE_COUNT; | ||
392 | l1fsm_s.event_count = L1_EVENT_COUNT; | ||
393 | l1fsm_s.strEvent = strL1Event; | ||
394 | l1fsm_s.strState = strL1SState; | ||
395 | mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | void | ||
400 | l1_cleanup(void) | ||
401 | { | ||
402 | mISDN_FsmFree(&l1fsm_s); | ||
403 | } | ||