Mention branches and keyring.
[releases.git] / fddi / skfp / ecm.c
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,event) ;
124                 event = 0 ;
125         } while (state != smc->mib.fddiSMTECMState) ;
126         ecm_state_change(smc,(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,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,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,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,smc->s.ecm_check_poll,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,smc->s.ecm_check_poll,0) ;
386                 }
387                 break ;
388         case ACTIONS(EC7_DEINSERT) :
389                 sm_pm_bypass_req(smc,BP_DEINSERT);
390                 start_ecm_timer(smc,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,&smc->e.ecm_timer,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,&smc->e.ecm_timer) ;
524 }