1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Author Karsten Keil <kkeil@novell.com> |
5 | * |
6 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> |
7 | */ |
8 | |
9 | |
10 | #include <linux/slab.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mISDNhw.h> |
13 | #include "core.h" |
14 | #include "layer1.h" |
15 | #include "fsm.h" |
16 | |
17 | static u_int *debug; |
18 | |
19 | struct layer1 { |
20 | u_long Flags; |
21 | struct FsmInst l1m; |
22 | struct FsmTimer timer3; |
23 | struct FsmTimer timerX; |
24 | int delay; |
25 | int t3_value; |
26 | struct dchannel *dch; |
27 | dchannel_l1callback *dcb; |
28 | }; |
29 | |
30 | #define TIMER3_DEFAULT_VALUE 7000 |
31 | |
32 | static |
33 | struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL}; |
34 | |
35 | enum { |
36 | ST_L1_F2, |
37 | ST_L1_F3, |
38 | ST_L1_F4, |
39 | ST_L1_F5, |
40 | ST_L1_F6, |
41 | ST_L1_F7, |
42 | ST_L1_F8, |
43 | }; |
44 | |
45 | #define L1S_STATE_COUNT (ST_L1_F8 + 1) |
46 | |
47 | static char *strL1SState[] = |
48 | { |
49 | "ST_L1_F2" , |
50 | "ST_L1_F3" , |
51 | "ST_L1_F4" , |
52 | "ST_L1_F5" , |
53 | "ST_L1_F6" , |
54 | "ST_L1_F7" , |
55 | "ST_L1_F8" , |
56 | }; |
57 | |
58 | enum { |
59 | EV_PH_ACTIVATE, |
60 | EV_PH_DEACTIVATE, |
61 | EV_RESET_IND, |
62 | EV_DEACT_CNF, |
63 | EV_DEACT_IND, |
64 | EV_POWER_UP, |
65 | EV_ANYSIG_IND, |
66 | EV_INFO2_IND, |
67 | EV_INFO4_IND, |
68 | EV_TIMER_DEACT, |
69 | EV_TIMER_ACT, |
70 | EV_TIMER3, |
71 | }; |
72 | |
73 | #define L1_EVENT_COUNT (EV_TIMER3 + 1) |
74 | |
75 | static char *strL1Event[] = |
76 | { |
77 | "EV_PH_ACTIVATE" , |
78 | "EV_PH_DEACTIVATE" , |
79 | "EV_RESET_IND" , |
80 | "EV_DEACT_CNF" , |
81 | "EV_DEACT_IND" , |
82 | "EV_POWER_UP" , |
83 | "EV_ANYSIG_IND" , |
84 | "EV_INFO2_IND" , |
85 | "EV_INFO4_IND" , |
86 | "EV_TIMER_DEACT" , |
87 | "EV_TIMER_ACT" , |
88 | "EV_TIMER3" , |
89 | }; |
90 | |
91 | static void |
92 | l1m_debug(struct FsmInst *fi, char *fmt, ...) |
93 | { |
94 | struct layer1 *l1 = fi->userdata; |
95 | struct va_format vaf; |
96 | va_list va; |
97 | |
98 | va_start(va, fmt); |
99 | |
100 | vaf.fmt = fmt; |
101 | vaf.va = &va; |
102 | |
103 | printk(KERN_DEBUG "%s: %pV\n" , dev_name(&l1->dch->dev.dev), &vaf); |
104 | |
105 | va_end(va); |
106 | } |
107 | |
108 | static void |
109 | l1_reset(struct FsmInst *fi, int event, void *arg) |
110 | { |
111 | mISDN_FsmChangeState(fi, ST_L1_F3); |
112 | } |
113 | |
114 | static void |
115 | l1_deact_cnf(struct FsmInst *fi, int event, void *arg) |
116 | { |
117 | struct layer1 *l1 = fi->userdata; |
118 | |
119 | mISDN_FsmChangeState(fi, ST_L1_F3); |
120 | if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) |
121 | l1->dcb(l1->dch, HW_POWERUP_REQ); |
122 | } |
123 | |
124 | static void |
125 | l1_deact_req_s(struct FsmInst *fi, int event, void *arg) |
126 | { |
127 | struct layer1 *l1 = fi->userdata; |
128 | |
129 | mISDN_FsmChangeState(fi, ST_L1_F3); |
130 | mISDN_FsmRestartTimer(&l1->timerX, 550, EV_TIMER_DEACT, NULL, 2); |
131 | test_and_set_bit(FLG_L1_DEACTTIMER, addr: &l1->Flags); |
132 | } |
133 | |
134 | static void |
135 | l1_power_up_s(struct FsmInst *fi, int event, void *arg) |
136 | { |
137 | struct layer1 *l1 = fi->userdata; |
138 | |
139 | if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) { |
140 | mISDN_FsmChangeState(fi, ST_L1_F4); |
141 | l1->dcb(l1->dch, INFO3_P8); |
142 | } else |
143 | mISDN_FsmChangeState(fi, ST_L1_F3); |
144 | } |
145 | |
146 | static void |
147 | l1_go_F5(struct FsmInst *fi, int event, void *arg) |
148 | { |
149 | mISDN_FsmChangeState(fi, ST_L1_F5); |
150 | } |
151 | |
152 | static void |
153 | l1_go_F8(struct FsmInst *fi, int event, void *arg) |
154 | { |
155 | mISDN_FsmChangeState(fi, ST_L1_F8); |
156 | } |
157 | |
158 | static void |
159 | l1_info2_ind(struct FsmInst *fi, int event, void *arg) |
160 | { |
161 | struct layer1 *l1 = fi->userdata; |
162 | |
163 | mISDN_FsmChangeState(fi, ST_L1_F6); |
164 | l1->dcb(l1->dch, INFO3_P8); |
165 | } |
166 | |
167 | static void |
168 | l1_info4_ind(struct FsmInst *fi, int event, void *arg) |
169 | { |
170 | struct layer1 *l1 = fi->userdata; |
171 | |
172 | mISDN_FsmChangeState(fi, ST_L1_F7); |
173 | l1->dcb(l1->dch, INFO3_P8); |
174 | if (test_and_clear_bit(FLG_L1_DEACTTIMER, addr: &l1->Flags)) |
175 | mISDN_FsmDelTimer(&l1->timerX, 4); |
176 | if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) { |
177 | if (test_and_clear_bit(FLG_L1_T3RUN, addr: &l1->Flags)) |
178 | mISDN_FsmDelTimer(&l1->timer3, 3); |
179 | mISDN_FsmRestartTimer(&l1->timerX, 110, EV_TIMER_ACT, NULL, 2); |
180 | test_and_set_bit(FLG_L1_ACTTIMER, addr: &l1->Flags); |
181 | } |
182 | } |
183 | |
184 | static void |
185 | l1_timer3(struct FsmInst *fi, int event, void *arg) |
186 | { |
187 | struct layer1 *l1 = fi->userdata; |
188 | |
189 | test_and_clear_bit(FLG_L1_T3RUN, addr: &l1->Flags); |
190 | if (test_and_clear_bit(FLG_L1_ACTIVATING, addr: &l1->Flags)) { |
191 | if (test_and_clear_bit(FLG_L1_DBLOCKED, addr: &l1->Flags)) |
192 | l1->dcb(l1->dch, HW_D_NOBLOCKED); |
193 | l1->dcb(l1->dch, PH_DEACTIVATE_IND); |
194 | } |
195 | if (l1->l1m.state != ST_L1_F6) { |
196 | mISDN_FsmChangeState(fi, ST_L1_F3); |
197 | /* do not force anything here, we need send INFO 0 */ |
198 | } |
199 | } |
200 | |
201 | static void |
202 | l1_timer_act(struct FsmInst *fi, int event, void *arg) |
203 | { |
204 | struct layer1 *l1 = fi->userdata; |
205 | |
206 | test_and_clear_bit(FLG_L1_ACTTIMER, addr: &l1->Flags); |
207 | test_and_set_bit(FLG_L1_ACTIVATED, addr: &l1->Flags); |
208 | l1->dcb(l1->dch, PH_ACTIVATE_IND); |
209 | } |
210 | |
211 | static void |
212 | l1_timer_deact(struct FsmInst *fi, int event, void *arg) |
213 | { |
214 | struct layer1 *l1 = fi->userdata; |
215 | |
216 | test_and_clear_bit(FLG_L1_DEACTTIMER, addr: &l1->Flags); |
217 | test_and_clear_bit(FLG_L1_ACTIVATED, addr: &l1->Flags); |
218 | if (test_and_clear_bit(FLG_L1_DBLOCKED, addr: &l1->Flags)) |
219 | l1->dcb(l1->dch, HW_D_NOBLOCKED); |
220 | l1->dcb(l1->dch, PH_DEACTIVATE_IND); |
221 | l1->dcb(l1->dch, HW_DEACT_REQ); |
222 | } |
223 | |
224 | static void |
225 | l1_activate_s(struct FsmInst *fi, int event, void *arg) |
226 | { |
227 | struct layer1 *l1 = fi->userdata; |
228 | |
229 | mISDN_FsmRestartTimer(&l1->timer3, l1->t3_value, EV_TIMER3, NULL, 2); |
230 | test_and_set_bit(FLG_L1_T3RUN, addr: &l1->Flags); |
231 | /* Tell HW to send INFO 1 */ |
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, addr: &l1->Flags); |
243 | if (test_and_clear_bit(FLG_L1_DBLOCKED, addr: &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->timerX, 0); |
300 | mISDN_FsmDelTimer(&l1->timer3, 0); |
301 | if (l1->dch) |
302 | l1->dch->l1 = NULL; |
303 | module_put(THIS_MODULE); |
304 | kfree(objp: l1); |
305 | } |
306 | |
307 | int |
308 | l1_event(struct layer1 *l1, u_int event) |
309 | { |
310 | int err = 0; |
311 | |
312 | if (!l1) |
313 | return -EINVAL; |
314 | switch (event) { |
315 | case HW_RESET_IND: |
316 | mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL); |
317 | break; |
318 | case HW_DEACT_IND: |
319 | mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL); |
320 | break; |
321 | case HW_POWERUP_IND: |
322 | mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL); |
323 | break; |
324 | case HW_DEACT_CNF: |
325 | mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL); |
326 | break; |
327 | case ANYSIGNAL: |
328 | mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); |
329 | break; |
330 | case LOSTFRAMING: |
331 | mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL); |
332 | break; |
333 | case INFO2: |
334 | mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL); |
335 | break; |
336 | case INFO4_P8: |
337 | mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); |
338 | break; |
339 | case INFO4_P10: |
340 | mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL); |
341 | break; |
342 | case PH_ACTIVATE_REQ: |
343 | if (test_bit(FLG_L1_ACTIVATED, &l1->Flags)) |
344 | l1->dcb(l1->dch, PH_ACTIVATE_IND); |
345 | else { |
346 | test_and_set_bit(FLG_L1_ACTIVATING, addr: &l1->Flags); |
347 | mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL); |
348 | } |
349 | break; |
350 | case CLOSE_CHANNEL: |
351 | release_l1(l1); |
352 | break; |
353 | default: |
354 | if ((event & ~HW_TIMER3_VMASK) == HW_TIMER3_VALUE) { |
355 | int val = event & HW_TIMER3_VMASK; |
356 | |
357 | if (val < 5) |
358 | val = 5; |
359 | if (val > 30) |
360 | val = 30; |
361 | l1->t3_value = val; |
362 | break; |
363 | } |
364 | if (*debug & DEBUG_L1) |
365 | printk(KERN_DEBUG "%s %x unhandled\n" , |
366 | __func__, event); |
367 | err = -EINVAL; |
368 | } |
369 | return err; |
370 | } |
371 | EXPORT_SYMBOL(l1_event); |
372 | |
373 | int |
374 | create_l1(struct dchannel *dch, dchannel_l1callback *dcb) { |
375 | struct layer1 *nl1; |
376 | |
377 | nl1 = kzalloc(size: sizeof(struct layer1), GFP_ATOMIC); |
378 | if (!nl1) { |
379 | printk(KERN_ERR "kmalloc struct layer1 failed\n" ); |
380 | return -ENOMEM; |
381 | } |
382 | nl1->l1m.fsm = &l1fsm_s; |
383 | nl1->l1m.state = ST_L1_F3; |
384 | nl1->Flags = 0; |
385 | nl1->t3_value = TIMER3_DEFAULT_VALUE; |
386 | nl1->l1m.debug = *debug & DEBUG_L1_FSM; |
387 | nl1->l1m.userdata = nl1; |
388 | nl1->l1m.userint = 0; |
389 | nl1->l1m.printdebug = l1m_debug; |
390 | nl1->dch = dch; |
391 | nl1->dcb = dcb; |
392 | mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer3); |
393 | mISDN_FsmInitTimer(&nl1->l1m, &nl1->timerX); |
394 | __module_get(THIS_MODULE); |
395 | dch->l1 = nl1; |
396 | return 0; |
397 | } |
398 | EXPORT_SYMBOL(create_l1); |
399 | |
400 | int |
401 | Isdnl1_Init(u_int *deb) |
402 | { |
403 | debug = deb; |
404 | l1fsm_s.state_count = L1S_STATE_COUNT; |
405 | l1fsm_s.event_count = L1_EVENT_COUNT; |
406 | l1fsm_s.strEvent = strL1Event; |
407 | l1fsm_s.strState = strL1SState; |
408 | return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); |
409 | } |
410 | |
411 | void |
412 | Isdnl1_cleanup(void) |
413 | { |
414 | mISDN_FsmFree(&l1fsm_s); |
415 | } |
416 | |