1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /******************************************************************************
4 * (C)Copyright 1998,1999 SysKonnect,
5 * a business unit of Schneider & Koch & Co. Datensysteme GmbH.
7 * See the file "skfddi.c" for further information.
9 * The information in this file is provided "AS IS" without warranty.
11 ******************************************************************************/
15 Configuration Management
20 * Hardware independent state machine implemantation
21 * The following external SMT functions are referenced :
25 * The following external HW dependent functions are referenced :
28 * The following HW dependent events are required :
37 #include "h/smtstate.h"
40 static const char ID_sccs
[] = "@(#)cfm.c 2.18 98/10/06 (C) SK " ;
47 #define GO_STATE(x) (smc->mib.fddiSMTCF_State = (x)|AFLAG)
48 #define ACTIONS_DONE() (smc->mib.fddiSMTCF_State &= ~AFLAG)
49 #define ACTIONS(x) (x|AFLAG)
52 * symbolic state names
54 static const char * const cfm_states
[] = {
55 "SC0_ISOLATED","CF1","CF2","CF3","CF4",
56 "SC1_WRAP_A","SC2_WRAP_B","SC5_TRHU_B","SC7_WRAP_S",
57 "SC9_C_WRAP_A","SC10_C_WRAP_B","SC11_C_WRAP_S","SC4_THRU_A"
61 * symbolic event names
63 static const char * const cfm_events
[] = {
64 "NONE","CF_LOOP_A","CF_LOOP_B","CF_JOIN_A","CF_JOIN_B"
68 * map from state to downstream port type
70 static const unsigned char cf_to_ptype
[] = {
71 TNONE
,TNONE
,TNONE
,TNONE
,TNONE
,
79 #define CEM_PST_DOWN 0
81 #define CEM_PST_HOLD 2
82 /* define portstate array only for A and B port */
83 /* Do this within the smc structure (use in multiple cards) */
86 * all Globals are defined in smc.h
91 * function declarations
93 static void cfm_fsm(struct s_smc
*smc
, int cmd
);
96 init CFM state machine
97 clear all CFM vars and flags
99 void cfm_init(struct s_smc
*smc
)
101 smc
->mib
.fddiSMTCF_State
= ACTIONS(SC0_ISOLATED
) ;
104 smc
->y
[PA
].scrub
= 0 ;
105 smc
->y
[PB
].scrub
= 0 ;
106 smc
->y
[PA
].cem_pst
= CEM_PST_DOWN
;
107 smc
->y
[PB
].cem_pst
= CEM_PST_DOWN
;
110 /* Some terms conditions used by the selection criteria */
111 #define THRU_ENABLED(smc) (smc->y[PA].pc_mode != PM_TREE && \
112 smc->y[PB].pc_mode != PM_TREE)
113 /* Selection criteria for the ports */
114 static void selection_criteria (struct s_smc
*smc
, struct s_phy
*phy
)
117 switch (phy
->mib
->fddiPORTMy_Type
) {
119 if ( !THRU_ENABLED(smc
) && smc
->y
[PB
].cf_join
) {
120 phy
->wc_flag
= TRUE
;
122 phy
->wc_flag
= FALSE
;
127 /* take precedence over PA */
128 phy
->wc_flag
= FALSE
;
131 phy
->wc_flag
= FALSE
;
134 phy
->wc_flag
= FALSE
;
140 void all_selection_criteria(struct s_smc
*smc
)
145 for ( p
= 0,phy
= smc
->y
; p
< NUMPHYS
; p
++, phy
++ ) {
146 /* Do the selection criteria */
147 selection_criteria (smc
,phy
);
151 static void cem_priv_state(struct s_smc
*smc
, int event
)
152 /* State machine for private PORT states: used to optimize dual homing */
154 int np
; /* Number of the port */
157 /* Do this only in a DAS */
158 if (smc
->s
.sas
!= SMT_DAS
)
161 np
= event
- CF_JOIN
;
163 if (np
!= PA
&& np
!= PB
) {
166 /* Change the port state according to the event (portnumber) */
167 if (smc
->y
[np
].cf_join
) {
168 smc
->y
[np
].cem_pst
= CEM_PST_UP
;
169 } else if (!smc
->y
[np
].wc_flag
) {
170 /* set the port to done only if it is not withheld */
171 smc
->y
[np
].cem_pst
= CEM_PST_DOWN
;
174 /* Don't set an hold port to down */
176 /* Check all ports of restart conditions */
177 for (i
= 0 ; i
< 2 ; i
++ ) {
178 /* Check all port for PORT is on hold and no withhold is done */
179 if ( smc
->y
[i
].cem_pst
== CEM_PST_HOLD
&& !smc
->y
[i
].wc_flag
) {
180 smc
->y
[i
].cem_pst
= CEM_PST_DOWN
;
181 queue_event(smc
,(int)(EVENT_PCM
+i
),PC_START
) ;
183 if ( smc
->y
[i
].cem_pst
== CEM_PST_UP
&& smc
->y
[i
].wc_flag
) {
184 smc
->y
[i
].cem_pst
= CEM_PST_HOLD
;
185 queue_event(smc
,(int)(EVENT_PCM
+i
),PC_START
) ;
187 if ( smc
->y
[i
].cem_pst
== CEM_PST_DOWN
&& smc
->y
[i
].wc_flag
) {
189 * The port must be restarted when the wc_flag
190 * will be reset. So set the port on hold.
192 smc
->y
[i
].cem_pst
= CEM_PST_HOLD
;
207 void cfm(struct s_smc
*smc
, int event
)
209 int state
; /* remember last state */
213 /* We will do the following: */
214 /* - compute the variable WC_Flag for every port (This is where */
215 /* we can extend the requested path checking !!) */
216 /* - do the old (SMT 6.2 like) state machine */
217 /* - do the resulting station states */
219 all_selection_criteria (smc
);
221 /* We will check now whether a state transition is allowed or not */
222 /* - change the portstates */
223 cem_priv_state (smc
, event
);
225 oldstate
= smc
->mib
.fddiSMTCF_State
;
227 DB_CFM("CFM : state %s%s event %s",
228 smc
->mib
.fddiSMTCF_State
& AFLAG
? "ACTIONS " : "",
229 cfm_states
[smc
->mib
.fddiSMTCF_State
& ~AFLAG
],
231 state
= smc
->mib
.fddiSMTCF_State
;
234 } while (state
!= smc
->mib
.fddiSMTCF_State
) ;
238 * check peer wrap condition
241 if ( (smc
->mib
.fddiSMTCF_State
== SC9_C_WRAP_A
&&
242 smc
->y
[PA
].pc_mode
== PM_PEER
) ||
243 (smc
->mib
.fddiSMTCF_State
== SC10_C_WRAP_B
&&
244 smc
->y
[PB
].pc_mode
== PM_PEER
) ||
245 (smc
->mib
.fddiSMTCF_State
== SC11_C_WRAP_S
&&
246 smc
->y
[PS
].pc_mode
== PM_PEER
&&
247 smc
->y
[PS
].mib
->fddiPORTNeighborType
!= TS
) ) {
250 if (cond
!= smc
->mib
.fddiSMTPeerWrapFlag
)
251 smt_srf_event(smc
,SMT_COND_SMT_PEER_WRAP
,0,cond
) ;
255 * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired
256 * to the primary path.
261 if (smc
->mib
.fddiSMTCF_State
!= oldstate
) {
262 smt_srf_event(smc
,SMT_EVENT_MAC_PATH_CHANGE
,INDEX_MAC
,0) ;
265 #endif /* no SLIM_SMT */
270 smc
->mib
.m
[MAC0
].fddiMACDownstreamPORTType
=
271 cf_to_ptype
[smc
->mib
.fddiSMTCF_State
] ;
272 cfm_state_change(smc
,(int)smc
->mib
.fddiSMTCF_State
) ;
279 static void cfm_fsm(struct s_smc
*smc
, int cmd
)
281 switch(smc
->mib
.fddiSMTCF_State
) {
282 case ACTIONS(SC0_ISOLATED
) :
283 smc
->mib
.p
[PA
].fddiPORTCurrentPath
= MIB_PATH_ISOLATED
;
284 smc
->mib
.p
[PB
].fddiPORTCurrentPath
= MIB_PATH_ISOLATED
;
285 smc
->mib
.p
[PA
].fddiPORTMACPlacement
= 0 ;
286 smc
->mib
.p
[PB
].fddiPORTMACPlacement
= 0 ;
287 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_SEPA
;
288 config_mux(smc
,MUX_ISOLATE
) ; /* configure PHY Mux */
289 smc
->r
.rm_loop
= FALSE
;
290 smc
->r
.rm_join
= FALSE
;
291 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
292 /* Don't do the WC-Flag changing here */
294 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
298 /*SAS port can be PA or PB ! */
299 if (smc
->s
.sas
&& (smc
->y
[PA
].cf_join
|| smc
->y
[PA
].cf_loop
||
300 smc
->y
[PB
].cf_join
|| smc
->y
[PB
].cf_loop
)) {
301 GO_STATE(SC11_C_WRAP_S
) ;
305 if ((smc
->y
[PA
].cem_pst
== CEM_PST_UP
&& smc
->y
[PA
].cf_join
&&
306 !smc
->y
[PA
].wc_flag
) || smc
->y
[PA
].cf_loop
) {
307 GO_STATE(SC9_C_WRAP_A
) ;
311 if ((smc
->y
[PB
].cem_pst
== CEM_PST_UP
&& smc
->y
[PB
].cf_join
&&
312 !smc
->y
[PB
].wc_flag
) || smc
->y
[PB
].cf_loop
) {
313 GO_STATE(SC10_C_WRAP_B
) ;
317 case ACTIONS(SC9_C_WRAP_A
) :
318 smc
->mib
.p
[PA
].fddiPORTCurrentPath
= MIB_PATH_CONCATENATED
;
319 smc
->mib
.p
[PB
].fddiPORTCurrentPath
= MIB_PATH_ISOLATED
;
320 smc
->mib
.p
[PA
].fddiPORTMACPlacement
= INDEX_MAC
;
321 smc
->mib
.p
[PB
].fddiPORTMACPlacement
= 0 ;
322 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_CON
;
323 config_mux(smc
,MUX_WRAPA
) ; /* configure PHY mux */
324 if (smc
->y
[PA
].cf_loop
) {
325 smc
->r
.rm_join
= FALSE
;
326 smc
->r
.rm_loop
= TRUE
;
327 queue_event(smc
,EVENT_RMT
,RM_LOOP
) ;/* signal RMT */
329 if (smc
->y
[PA
].cf_join
) {
330 smc
->r
.rm_loop
= FALSE
;
331 smc
->r
.rm_join
= TRUE
;
332 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
335 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
339 if ( (smc
->y
[PA
].wc_flag
|| !smc
->y
[PA
].cf_join
) &&
340 !smc
->y
[PA
].cf_loop
) {
341 GO_STATE(SC0_ISOLATED
) ;
345 else if ( (smc
->y
[PB
].cf_loop
&& smc
->y
[PA
].cf_join
&&
346 smc
->y
[PA
].cem_pst
== CEM_PST_UP
) ||
347 ((smc
->y
[PB
].cf_loop
||
348 (smc
->y
[PB
].cf_join
&&
349 smc
->y
[PB
].cem_pst
== CEM_PST_UP
)) &&
350 (smc
->y
[PA
].pc_mode
== PM_TREE
||
351 smc
->y
[PB
].pc_mode
== PM_TREE
))) {
352 smc
->y
[PA
].scrub
= TRUE
;
353 GO_STATE(SC10_C_WRAP_B
) ;
357 else if (!smc
->s
.attach_s
&&
358 smc
->y
[PA
].cf_join
&&
359 smc
->y
[PA
].cem_pst
== CEM_PST_UP
&&
360 smc
->y
[PA
].pc_mode
== PM_PEER
&& smc
->y
[PB
].cf_join
&&
361 smc
->y
[PB
].cem_pst
== CEM_PST_UP
&&
362 smc
->y
[PB
].pc_mode
== PM_PEER
) {
363 smc
->y
[PA
].scrub
= TRUE
;
364 smc
->y
[PB
].scrub
= TRUE
;
365 GO_STATE(SC4_THRU_A
) ;
369 else if ( smc
->s
.attach_s
&&
370 smc
->y
[PA
].cf_join
&&
371 smc
->y
[PA
].cem_pst
== CEM_PST_UP
&&
372 smc
->y
[PA
].pc_mode
== PM_PEER
&&
373 smc
->y
[PB
].cf_join
&&
374 smc
->y
[PB
].cem_pst
== CEM_PST_UP
&&
375 smc
->y
[PB
].pc_mode
== PM_PEER
) {
376 smc
->y
[PA
].scrub
= TRUE
;
377 smc
->y
[PB
].scrub
= TRUE
;
378 GO_STATE(SC5_THRU_B
) ;
382 case ACTIONS(SC10_C_WRAP_B
) :
383 smc
->mib
.p
[PA
].fddiPORTCurrentPath
= MIB_PATH_ISOLATED
;
384 smc
->mib
.p
[PB
].fddiPORTCurrentPath
= MIB_PATH_CONCATENATED
;
385 smc
->mib
.p
[PA
].fddiPORTMACPlacement
= 0 ;
386 smc
->mib
.p
[PB
].fddiPORTMACPlacement
= INDEX_MAC
;
387 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_CON
;
388 config_mux(smc
,MUX_WRAPB
) ; /* configure PHY mux */
389 if (smc
->y
[PB
].cf_loop
) {
390 smc
->r
.rm_join
= FALSE
;
391 smc
->r
.rm_loop
= TRUE
;
392 queue_event(smc
,EVENT_RMT
,RM_LOOP
) ;/* signal RMT */
394 if (smc
->y
[PB
].cf_join
) {
395 smc
->r
.rm_loop
= FALSE
;
396 smc
->r
.rm_join
= TRUE
;
397 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
400 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
404 if ( !smc
->y
[PB
].cf_join
&& !smc
->y
[PB
].cf_loop
) {
405 GO_STATE(SC0_ISOLATED
) ;
409 else if ( smc
->y
[PA
].cf_loop
&& smc
->y
[PA
].pc_mode
== PM_PEER
&&
410 smc
->y
[PB
].cf_join
&& smc
->y
[PB
].pc_mode
== PM_PEER
) {
411 smc
->y
[PB
].scrub
= TRUE
;
412 GO_STATE(SC9_C_WRAP_A
) ;
416 else if (!smc
->s
.attach_s
&&
417 smc
->y
[PA
].cf_join
&& smc
->y
[PA
].pc_mode
== PM_PEER
&&
418 smc
->y
[PB
].cf_join
&& smc
->y
[PB
].pc_mode
== PM_PEER
) {
419 smc
->y
[PA
].scrub
= TRUE
;
420 smc
->y
[PB
].scrub
= TRUE
;
421 GO_STATE(SC4_THRU_A
) ;
425 else if ( smc
->s
.attach_s
&&
426 smc
->y
[PA
].cf_join
&& smc
->y
[PA
].pc_mode
== PM_PEER
&&
427 smc
->y
[PB
].cf_join
&& smc
->y
[PB
].pc_mode
== PM_PEER
) {
428 smc
->y
[PA
].scrub
= TRUE
;
429 smc
->y
[PB
].scrub
= TRUE
;
430 GO_STATE(SC5_THRU_B
) ;
434 case ACTIONS(SC4_THRU_A
) :
435 smc
->mib
.p
[PA
].fddiPORTCurrentPath
= MIB_PATH_THRU
;
436 smc
->mib
.p
[PB
].fddiPORTCurrentPath
= MIB_PATH_THRU
;
437 smc
->mib
.p
[PA
].fddiPORTMACPlacement
= 0 ;
438 smc
->mib
.p
[PB
].fddiPORTMACPlacement
= INDEX_MAC
;
439 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_THRU
;
440 config_mux(smc
,MUX_THRUA
) ; /* configure PHY mux */
441 smc
->r
.rm_loop
= FALSE
;
442 smc
->r
.rm_join
= TRUE
;
443 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
445 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
449 if (smc
->y
[PB
].wc_flag
|| !smc
->y
[PB
].cf_join
) {
450 smc
->y
[PA
].scrub
= TRUE
;
451 GO_STATE(SC9_C_WRAP_A
) ;
455 else if (!smc
->y
[PA
].cf_join
|| smc
->y
[PA
].wc_flag
) {
456 smc
->y
[PB
].scrub
= TRUE
;
457 GO_STATE(SC10_C_WRAP_B
) ;
461 else if (smc
->s
.attach_s
) {
462 smc
->y
[PB
].scrub
= TRUE
;
463 GO_STATE(SC5_THRU_B
) ;
467 case ACTIONS(SC5_THRU_B
) :
468 smc
->mib
.p
[PA
].fddiPORTCurrentPath
= MIB_PATH_THRU
;
469 smc
->mib
.p
[PB
].fddiPORTCurrentPath
= MIB_PATH_THRU
;
470 smc
->mib
.p
[PA
].fddiPORTMACPlacement
= INDEX_MAC
;
471 smc
->mib
.p
[PB
].fddiPORTMACPlacement
= 0 ;
472 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_THRU
;
473 config_mux(smc
,MUX_THRUB
) ; /* configure PHY mux */
474 smc
->r
.rm_loop
= FALSE
;
475 smc
->r
.rm_join
= TRUE
;
476 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
478 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
482 if (!smc
->y
[PB
].cf_join
|| smc
->y
[PB
].wc_flag
) {
483 smc
->y
[PA
].scrub
= TRUE
;
484 GO_STATE(SC9_C_WRAP_A
) ;
488 else if (!smc
->y
[PA
].cf_join
|| smc
->y
[PA
].wc_flag
) {
489 smc
->y
[PB
].scrub
= TRUE
;
490 GO_STATE(SC10_C_WRAP_B
) ;
494 else if (!smc
->s
.attach_s
) {
495 smc
->y
[PA
].scrub
= TRUE
;
496 GO_STATE(SC4_THRU_A
) ;
500 case ACTIONS(SC11_C_WRAP_S
) :
501 smc
->mib
.p
[PS
].fddiPORTCurrentPath
= MIB_PATH_CONCATENATED
;
502 smc
->mib
.p
[PS
].fddiPORTMACPlacement
= INDEX_MAC
;
503 smc
->mib
.fddiSMTStationStatus
= MIB_SMT_STASTA_CON
;
504 config_mux(smc
,MUX_WRAPS
) ; /* configure PHY mux */
505 if (smc
->y
[PA
].cf_loop
|| smc
->y
[PB
].cf_loop
) {
506 smc
->r
.rm_join
= FALSE
;
507 smc
->r
.rm_loop
= TRUE
;
508 queue_event(smc
,EVENT_RMT
,RM_LOOP
) ;/* signal RMT */
510 if (smc
->y
[PA
].cf_join
|| smc
->y
[PB
].cf_join
) {
511 smc
->r
.rm_loop
= FALSE
;
512 smc
->r
.rm_join
= TRUE
;
513 queue_event(smc
,EVENT_RMT
,RM_JOIN
) ;/* signal RMT */
516 DB_CFMN(1, "CFM : %s", cfm_states
[smc
->mib
.fddiSMTCF_State
]);
520 if ( !smc
->y
[PA
].cf_join
&& !smc
->y
[PA
].cf_loop
&&
521 !smc
->y
[PB
].cf_join
&& !smc
->y
[PB
].cf_loop
) {
522 GO_STATE(SC0_ISOLATED
) ;
527 SMT_PANIC(smc
,SMT_E0106
, SMT_E0106_MSG
) ;
533 * get MAC's input Port
537 int cfm_get_mac_input(struct s_smc
*smc
)
539 return (smc
->mib
.fddiSMTCF_State
== SC10_C_WRAP_B
||
540 smc
->mib
.fddiSMTCF_State
== SC5_THRU_B
) ? PB
: PA
;
544 * get MAC's output Port
548 int cfm_get_mac_output(struct s_smc
*smc
)
550 return (smc
->mib
.fddiSMTCF_State
== SC10_C_WRAP_B
||
551 smc
->mib
.fddiSMTCF_State
== SC4_THRU_A
) ? PB
: PA
;
554 static char path_iso
[] = {
555 0,0, 0,RES_PORT
, 0,PA
+ INDEX_PORT
, 0,PATH_ISO
,
556 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_ISO
,
557 0,0, 0,RES_PORT
, 0,PB
+ INDEX_PORT
, 0,PATH_ISO
560 static char path_wrap_a
[] = {
561 0,0, 0,RES_PORT
, 0,PA
+ INDEX_PORT
, 0,PATH_PRIM
,
562 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_PRIM
,
563 0,0, 0,RES_PORT
, 0,PB
+ INDEX_PORT
, 0,PATH_ISO
566 static char path_wrap_b
[] = {
567 0,0, 0,RES_PORT
, 0,PB
+ INDEX_PORT
, 0,PATH_PRIM
,
568 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_PRIM
,
569 0,0, 0,RES_PORT
, 0,PA
+ INDEX_PORT
, 0,PATH_ISO
572 static char path_thru
[] = {
573 0,0, 0,RES_PORT
, 0,PA
+ INDEX_PORT
, 0,PATH_PRIM
,
574 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_PRIM
,
575 0,0, 0,RES_PORT
, 0,PB
+ INDEX_PORT
, 0,PATH_PRIM
578 static char path_wrap_s
[] = {
579 0,0, 0,RES_PORT
, 0,PS
+ INDEX_PORT
, 0,PATH_PRIM
,
580 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_PRIM
,
583 static char path_iso_s
[] = {
584 0,0, 0,RES_PORT
, 0,PS
+ INDEX_PORT
, 0,PATH_ISO
,
585 0,0, 0,RES_MAC
, 0,INDEX_MAC
, 0,PATH_ISO
,
588 int cem_build_path(struct s_smc
*smc
, char *to
, int path_index
)
593 switch (smc
->mib
.fddiSMTCF_State
) {
596 path
= smc
->s
.sas
? path_iso_s
: path_iso
;
597 len
= smc
->s
.sas
? sizeof(path_iso_s
) : sizeof(path_iso
) ;
601 len
= sizeof(path_wrap_a
) ;
605 len
= sizeof(path_wrap_b
) ;
609 len
= sizeof(path_thru
) ;
613 len
= sizeof(path_wrap_s
) ;
616 memcpy(to
,path
,len
) ;
618 LINT_USE(path_index
);