1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /****************************************************************************** |
3 | * |
4 | * (C)Copyright 1998,1999 SysKonnect, |
5 | * a business unit of Schneider & Koch & Co. Datensysteme GmbH. |
6 | * |
7 | * See the file "skfddi.c" for further information. |
8 | * |
9 | * The information in this file is provided "AS IS" without warranty. |
10 | * |
11 | ******************************************************************************/ |
12 | |
13 | /* |
14 | SMT ECM |
15 | Entity Coordination Management |
16 | Hardware independent state machine |
17 | */ |
18 | |
19 | /* |
20 | * Hardware independent state machine implemantation |
21 | * The following external SMT functions are referenced : |
22 | * |
23 | * queue_event() |
24 | * smt_timer_start() |
25 | * smt_timer_stop() |
26 | * |
27 | * The following external HW dependent functions are referenced : |
28 | * sm_pm_bypass_req() |
29 | * sm_pm_get_ls() |
30 | * |
31 | * The following HW dependent events are required : |
32 | * NONE |
33 | * |
34 | */ |
35 | |
36 | #include "h/types.h" |
37 | #include "h/fddi.h" |
38 | #include "h/smc.h" |
39 | |
40 | #define KERNEL |
41 | #include "h/smtstate.h" |
42 | |
43 | /* |
44 | * FSM Macros |
45 | */ |
46 | #define AFLAG 0x10 |
47 | #define GO_STATE(x) (smc->mib.fddiSMTECMState = (x)|AFLAG) |
48 | #define ACTIONS_DONE() (smc->mib.fddiSMTECMState &= ~AFLAG) |
49 | #define ACTIONS(x) (x|AFLAG) |
50 | |
51 | #define EC0_OUT 0 /* not inserted */ |
52 | #define EC1_IN 1 /* inserted */ |
53 | #define EC2_TRACE 2 /* tracing */ |
54 | #define EC3_LEAVE 3 /* leaving the ring */ |
55 | #define EC4_PATH_TEST 4 /* performing path test */ |
56 | #define EC5_INSERT 5 /* bypass being turned on */ |
57 | #define EC6_CHECK 6 /* checking bypass */ |
58 | #define EC7_DEINSERT 7 /* bypass being turnde off */ |
59 | |
60 | /* |
61 | * symbolic state names |
62 | */ |
63 | static const char * const ecm_states[] = { |
64 | "EC0_OUT" ,"EC1_IN" ,"EC2_TRACE" ,"EC3_LEAVE" ,"EC4_PATH_TEST" , |
65 | "EC5_INSERT" ,"EC6_CHECK" ,"EC7_DEINSERT" |
66 | } ; |
67 | |
68 | /* |
69 | * symbolic event names |
70 | */ |
71 | static const char * const ecm_events[] = { |
72 | "NONE" ,"EC_CONNECT" ,"EC_DISCONNECT" ,"EC_TRACE_PROP" ,"EC_PATH_TEST" , |
73 | "EC_TIMEOUT_TD" ,"EC_TIMEOUT_TMAX" , |
74 | "EC_TIMEOUT_IMAX" ,"EC_TIMEOUT_INMAX" ,"EC_TEST_DONE" |
75 | } ; |
76 | |
77 | /* |
78 | * all Globals are defined in smc.h |
79 | * struct s_ecm |
80 | */ |
81 | |
82 | /* |
83 | * function declarations |
84 | */ |
85 | |
86 | static void ecm_fsm(struct s_smc *smc, int cmd); |
87 | static void start_ecm_timer(struct s_smc *smc, u_long value, int event); |
88 | static void stop_ecm_timer(struct s_smc *smc); |
89 | static void prop_actions(struct s_smc *smc); |
90 | |
91 | /* |
92 | init ECM state machine |
93 | clear all ECM vars and flags |
94 | */ |
95 | void ecm_init(struct s_smc *smc) |
96 | { |
97 | smc->e.path_test = PT_PASSED ; |
98 | smc->e.trace_prop = 0 ; |
99 | smc->e.sb_flag = 0 ; |
100 | smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ; |
101 | smc->e.ecm_line_state = FALSE ; |
102 | } |
103 | |
104 | /* |
105 | ECM state machine |
106 | called by dispatcher |
107 | |
108 | do |
109 | display state change |
110 | process event |
111 | until SM is stable |
112 | */ |
113 | void ecm(struct s_smc *smc, int event) |
114 | { |
115 | int state ; |
116 | |
117 | do { |
118 | DB_ECM("ECM : state %s%s event %s" , |
119 | smc->mib.fddiSMTECMState & AFLAG ? "ACTIONS " : "" , |
120 | ecm_states[smc->mib.fddiSMTECMState & ~AFLAG], |
121 | ecm_events[event]); |
122 | state = smc->mib.fddiSMTECMState ; |
123 | ecm_fsm(smc,cmd: event) ; |
124 | event = 0 ; |
125 | } while (state != smc->mib.fddiSMTECMState) ; |
126 | ecm_state_change(smc,e_state: (int)smc->mib.fddiSMTECMState) ; |
127 | } |
128 | |
129 | /* |
130 | process ECM event |
131 | */ |
132 | static void ecm_fsm(struct s_smc *smc, int cmd) |
133 | { |
134 | int ls_a ; /* current line state PHY A */ |
135 | int ls_b ; /* current line state PHY B */ |
136 | int p ; /* ports */ |
137 | |
138 | |
139 | smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ; |
140 | if (cmd == EC_CONNECT) |
141 | smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ; |
142 | |
143 | /* For AIX event notification: */ |
144 | /* Is a disconnect command remotely issued ? */ |
145 | if (cmd == EC_DISCONNECT && |
146 | smc->mib.fddiSMTRemoteDisconnectFlag == TRUE) { |
147 | AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long) |
148 | FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc), |
149 | smt_get_error_word(smc) ); |
150 | } |
151 | |
152 | /*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/ |
153 | if (cmd == EC_CONNECT) { |
154 | smc->e.DisconnectFlag = FALSE ; |
155 | } |
156 | else if (cmd == EC_DISCONNECT) { |
157 | smc->e.DisconnectFlag = TRUE ; |
158 | } |
159 | |
160 | switch(smc->mib.fddiSMTECMState) { |
161 | case ACTIONS(EC0_OUT) : |
162 | /* |
163 | * We do not perform a path test |
164 | */ |
165 | smc->e.path_test = PT_PASSED ; |
166 | smc->e.ecm_line_state = FALSE ; |
167 | stop_ecm_timer(smc) ; |
168 | ACTIONS_DONE() ; |
169 | break ; |
170 | case EC0_OUT: |
171 | /*EC01*/ |
172 | if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent |
173 | && smc->e.path_test==PT_PASSED) { |
174 | GO_STATE(EC1_IN) ; |
175 | break ; |
176 | } |
177 | /*EC05*/ |
178 | else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) && |
179 | smc->mib.fddiSMTBypassPresent && |
180 | (smc->s.sas == SMT_DAS)) { |
181 | GO_STATE(EC5_INSERT) ; |
182 | break ; |
183 | } |
184 | break; |
185 | case ACTIONS(EC1_IN) : |
186 | stop_ecm_timer(smc) ; |
187 | smc->e.trace_prop = 0 ; |
188 | sm_ma_control(smc,MA_TREQ) ; |
189 | for (p = 0 ; p < NUMPHYS ; p++) |
190 | if (smc->mib.p[p].fddiPORTHardwarePresent) |
191 | queue_event(smc,EVENT_PCMA+p,PC_START) ; |
192 | ACTIONS_DONE() ; |
193 | break ; |
194 | case EC1_IN: |
195 | /*EC12*/ |
196 | if (cmd == EC_TRACE_PROP) { |
197 | prop_actions(smc) ; |
198 | GO_STATE(EC2_TRACE) ; |
199 | break ; |
200 | } |
201 | /*EC13*/ |
202 | else if (cmd == EC_DISCONNECT) { |
203 | GO_STATE(EC3_LEAVE) ; |
204 | break ; |
205 | } |
206 | break; |
207 | case ACTIONS(EC2_TRACE) : |
208 | start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration), |
209 | EC_TIMEOUT_TMAX) ; |
210 | ACTIONS_DONE() ; |
211 | break ; |
212 | case EC2_TRACE : |
213 | /*EC22*/ |
214 | if (cmd == EC_TRACE_PROP) { |
215 | prop_actions(smc) ; |
216 | GO_STATE(EC2_TRACE) ; |
217 | break ; |
218 | } |
219 | /*EC23a*/ |
220 | else if (cmd == EC_DISCONNECT) { |
221 | smc->e.path_test = PT_EXITING ; |
222 | GO_STATE(EC3_LEAVE) ; |
223 | break ; |
224 | } |
225 | /*EC23b*/ |
226 | else if (smc->e.path_test == PT_PENDING) { |
227 | GO_STATE(EC3_LEAVE) ; |
228 | break ; |
229 | } |
230 | /*EC23c*/ |
231 | else if (cmd == EC_TIMEOUT_TMAX) { |
232 | /* Trace_Max is expired */ |
233 | /* -> send AIX_EVENT */ |
234 | AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, |
235 | (u_long) FDDI_SMT_ERROR, (u_long) |
236 | FDDI_TRACE_MAX, smt_get_error_word(smc)); |
237 | smc->e.path_test = PT_PENDING ; |
238 | GO_STATE(EC3_LEAVE) ; |
239 | break ; |
240 | } |
241 | break ; |
242 | case ACTIONS(EC3_LEAVE) : |
243 | start_ecm_timer(smc,value: smc->s.ecm_td_min,EC_TIMEOUT_TD) ; |
244 | for (p = 0 ; p < NUMPHYS ; p++) |
245 | queue_event(smc,EVENT_PCMA+p,PC_STOP) ; |
246 | ACTIONS_DONE() ; |
247 | break ; |
248 | case EC3_LEAVE: |
249 | /*EC30*/ |
250 | if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent && |
251 | (smc->e.path_test != PT_PENDING)) { |
252 | GO_STATE(EC0_OUT) ; |
253 | break ; |
254 | } |
255 | /*EC34*/ |
256 | else if (cmd == EC_TIMEOUT_TD && |
257 | (smc->e.path_test == PT_PENDING)) { |
258 | GO_STATE(EC4_PATH_TEST) ; |
259 | break ; |
260 | } |
261 | /*EC31*/ |
262 | else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) { |
263 | GO_STATE(EC1_IN) ; |
264 | break ; |
265 | } |
266 | /*EC33*/ |
267 | else if (cmd == EC_DISCONNECT && |
268 | smc->e.path_test == PT_PENDING) { |
269 | smc->e.path_test = PT_EXITING ; |
270 | /* |
271 | * stay in state - state will be left via timeout |
272 | */ |
273 | } |
274 | /*EC37*/ |
275 | else if (cmd == EC_TIMEOUT_TD && |
276 | smc->mib.fddiSMTBypassPresent && |
277 | smc->e.path_test != PT_PENDING) { |
278 | GO_STATE(EC7_DEINSERT) ; |
279 | break ; |
280 | } |
281 | break ; |
282 | case ACTIONS(EC4_PATH_TEST) : |
283 | stop_ecm_timer(smc) ; |
284 | smc->e.path_test = PT_TESTING ; |
285 | start_ecm_timer(smc,value: smc->s.ecm_test_done,EC_TEST_DONE) ; |
286 | /* now perform path test ... just a simulation */ |
287 | ACTIONS_DONE() ; |
288 | break ; |
289 | case EC4_PATH_TEST : |
290 | /* path test done delay */ |
291 | if (cmd == EC_TEST_DONE) |
292 | smc->e.path_test = PT_PASSED ; |
293 | |
294 | if (smc->e.path_test == PT_FAILED) |
295 | RS_SET(smc,RS_PATHTEST) ; |
296 | |
297 | /*EC40a*/ |
298 | if (smc->e.path_test == PT_FAILED && |
299 | !smc->mib.fddiSMTBypassPresent) { |
300 | GO_STATE(EC0_OUT) ; |
301 | break ; |
302 | } |
303 | /*EC40b*/ |
304 | else if (cmd == EC_DISCONNECT && |
305 | !smc->mib.fddiSMTBypassPresent) { |
306 | GO_STATE(EC0_OUT) ; |
307 | break ; |
308 | } |
309 | /*EC41*/ |
310 | else if (smc->e.path_test == PT_PASSED) { |
311 | GO_STATE(EC1_IN) ; |
312 | break ; |
313 | } |
314 | /*EC47a*/ |
315 | else if (smc->e.path_test == PT_FAILED && |
316 | smc->mib.fddiSMTBypassPresent) { |
317 | GO_STATE(EC7_DEINSERT) ; |
318 | break ; |
319 | } |
320 | /*EC47b*/ |
321 | else if (cmd == EC_DISCONNECT && |
322 | smc->mib.fddiSMTBypassPresent) { |
323 | GO_STATE(EC7_DEINSERT) ; |
324 | break ; |
325 | } |
326 | break ; |
327 | case ACTIONS(EC5_INSERT) : |
328 | sm_pm_bypass_req(smc,BP_INSERT); |
329 | start_ecm_timer(smc,value: smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ; |
330 | ACTIONS_DONE() ; |
331 | break ; |
332 | case EC5_INSERT : |
333 | /*EC56*/ |
334 | if (cmd == EC_TIMEOUT_INMAX) { |
335 | GO_STATE(EC6_CHECK) ; |
336 | break ; |
337 | } |
338 | /*EC57*/ |
339 | else if (cmd == EC_DISCONNECT) { |
340 | GO_STATE(EC7_DEINSERT) ; |
341 | break ; |
342 | } |
343 | break ; |
344 | case ACTIONS(EC6_CHECK) : |
345 | /* |
346 | * in EC6_CHECK, we *POLL* the line state ! |
347 | * check whether both bypass switches have switched. |
348 | */ |
349 | start_ecm_timer(smc,value: smc->s.ecm_check_poll,event: 0) ; |
350 | smc->e.ecm_line_state = TRUE ; /* flag to pcm: report Q/HLS */ |
351 | ACTIONS_DONE() ; |
352 | break ; |
353 | case EC6_CHECK : |
354 | ls_a = sm_pm_get_ls(smc,PA) ; |
355 | ls_b = sm_pm_get_ls(smc,PB) ; |
356 | |
357 | /*EC61*/ |
358 | if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) && |
359 | ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) { |
360 | smc->e.sb_flag = FALSE ; |
361 | smc->e.ecm_line_state = FALSE ; |
362 | GO_STATE(EC1_IN) ; |
363 | break ; |
364 | } |
365 | /*EC66*/ |
366 | else if (!smc->e.sb_flag && |
367 | (((ls_a == PC_ILS) && (ls_b == PC_QLS)) || |
368 | ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){ |
369 | smc->e.sb_flag = TRUE ; |
370 | DB_ECMN(1, "ECM : EC6_CHECK - stuck bypass" ); |
371 | AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) |
372 | FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK, |
373 | smt_get_error_word(smc)); |
374 | } |
375 | /*EC67*/ |
376 | else if (cmd == EC_DISCONNECT) { |
377 | smc->e.ecm_line_state = FALSE ; |
378 | GO_STATE(EC7_DEINSERT) ; |
379 | break ; |
380 | } |
381 | else { |
382 | /* |
383 | * restart poll |
384 | */ |
385 | start_ecm_timer(smc,value: smc->s.ecm_check_poll,event: 0) ; |
386 | } |
387 | break ; |
388 | case ACTIONS(EC7_DEINSERT) : |
389 | sm_pm_bypass_req(smc,BP_DEINSERT); |
390 | start_ecm_timer(smc,value: smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ; |
391 | ACTIONS_DONE() ; |
392 | break ; |
393 | case EC7_DEINSERT: |
394 | /*EC70*/ |
395 | if (cmd == EC_TIMEOUT_IMAX) { |
396 | GO_STATE(EC0_OUT) ; |
397 | break ; |
398 | } |
399 | /*EC75*/ |
400 | else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) { |
401 | GO_STATE(EC5_INSERT) ; |
402 | break ; |
403 | } |
404 | break; |
405 | default: |
406 | SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ; |
407 | break; |
408 | } |
409 | } |
410 | |
411 | #ifndef CONCENTRATOR |
412 | /* |
413 | * trace propagation actions for SAS & DAS |
414 | */ |
415 | static void prop_actions(struct s_smc *smc) |
416 | { |
417 | int port_in = 0 ; |
418 | int port_out = 0 ; |
419 | |
420 | RS_SET(smc,RS_EVENT) ; |
421 | switch (smc->s.sas) { |
422 | case SMT_SAS : |
423 | port_in = port_out = pcm_get_s_port(smc) ; |
424 | break ; |
425 | case SMT_DAS : |
426 | port_in = cfm_get_mac_input(smc) ; /* PA or PB */ |
427 | port_out = cfm_get_mac_output(smc) ; /* PA or PB */ |
428 | break ; |
429 | case SMT_NAC : |
430 | SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ; |
431 | return ; |
432 | } |
433 | |
434 | DB_ECM("ECM : prop_actions - trace_prop %lu" , smc->e.trace_prop); |
435 | DB_ECM("ECM : prop_actions - in %d out %d" , port_in, port_out); |
436 | |
437 | if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) { |
438 | /* trace initiatior */ |
439 | DB_ECM("ECM : initiate TRACE on PHY %c" , 'A' + port_in - PA); |
440 | queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ; |
441 | } |
442 | else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) && |
443 | port_out != PA) { |
444 | /* trace propagate upstream */ |
445 | DB_ECM("ECM : propagate TRACE on PHY B" ); |
446 | queue_event(smc,EVENT_PCMB,PC_TRACE) ; |
447 | } |
448 | else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) && |
449 | port_out != PB) { |
450 | /* trace propagate upstream */ |
451 | DB_ECM("ECM : propagate TRACE on PHY A" ); |
452 | queue_event(smc,EVENT_PCMA,PC_TRACE) ; |
453 | } |
454 | else { |
455 | /* signal trace termination */ |
456 | DB_ECM("ECM : TRACE terminated" ); |
457 | smc->e.path_test = PT_PENDING ; |
458 | } |
459 | smc->e.trace_prop = 0 ; |
460 | } |
461 | #else |
462 | /* |
463 | * trace propagation actions for Concentrator |
464 | */ |
465 | static void prop_actions(struct s_smc *smc) |
466 | { |
467 | int initiator ; |
468 | int upstream ; |
469 | int p ; |
470 | |
471 | RS_SET(smc,RS_EVENT) ; |
472 | while (smc->e.trace_prop) { |
473 | DB_ECM("ECM : prop_actions - trace_prop %d" , |
474 | smc->e.trace_prop); |
475 | |
476 | if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) { |
477 | initiator = ENTITY_MAC ; |
478 | smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ; |
479 | DB_ECM("ECM: MAC initiates trace" ); |
480 | } |
481 | else { |
482 | for (p = NUMPHYS-1 ; p >= 0 ; p--) { |
483 | if (smc->e.trace_prop & |
484 | ENTITY_BIT(ENTITY_PHY(p))) |
485 | break ; |
486 | } |
487 | initiator = ENTITY_PHY(p) ; |
488 | smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ; |
489 | } |
490 | upstream = cem_get_upstream(smc,initiator) ; |
491 | |
492 | if (upstream == ENTITY_MAC) { |
493 | /* signal trace termination */ |
494 | DB_ECM("ECM : TRACE terminated" ); |
495 | smc->e.path_test = PT_PENDING ; |
496 | } |
497 | else { |
498 | /* trace propagate upstream */ |
499 | DB_ECM("ECM : propagate TRACE on PHY %d" , upstream); |
500 | queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ; |
501 | } |
502 | } |
503 | } |
504 | #endif |
505 | |
506 | |
507 | /* |
508 | * SMT timer interface |
509 | * start ECM timer |
510 | */ |
511 | static void start_ecm_timer(struct s_smc *smc, u_long value, int event) |
512 | { |
513 | smt_timer_start(smc,timer: &smc->e.ecm_timer,time: value,EV_TOKEN(EVENT_ECM,event)); |
514 | } |
515 | |
516 | /* |
517 | * SMT timer interface |
518 | * stop ECM timer |
519 | */ |
520 | static void stop_ecm_timer(struct s_smc *smc) |
521 | { |
522 | if (smc->e.ecm_timer.tm_active) |
523 | smt_timer_stop(smc,timer: &smc->e.ecm_timer) ; |
524 | } |
525 | |