xref: /linux/drivers/net/fddi/skfp/cfm.c (revision 44f57d78)
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 CFM
15 	Configuration Management
16 	DAS with single MAC
17 */
18 
19 /*
20  *	Hardware independent state machine implemantation
21  *	The following external SMT functions are referenced :
22  *
23  *		queue_event()
24  *
25  *	The following external HW dependent functions are referenced :
26  *		config_mux()
27  *
28  *	The following HW dependent events are required :
29  *		NONE
30  */
31 
32 #include "h/types.h"
33 #include "h/fddi.h"
34 #include "h/smc.h"
35 
36 #define KERNEL
37 #include "h/smtstate.h"
38 
39 #ifndef	lint
40 static const char ID_sccs[] = "@(#)cfm.c	2.18 98/10/06 (C) SK " ;
41 #endif
42 
43 /*
44  * FSM Macros
45  */
46 #define AFLAG	0x10
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)
50 
51 /*
52  * symbolic state names
53  */
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"
58 } ;
59 
60 /*
61  * symbolic event names
62  */
63 static const char * const cfm_events[] = {
64 	"NONE","CF_LOOP_A","CF_LOOP_B","CF_JOIN_A","CF_JOIN_B"
65 } ;
66 
67 /*
68  * map from state to downstream port type
69  */
70 static const unsigned char cf_to_ptype[] = {
71 	TNONE,TNONE,TNONE,TNONE,TNONE,
72 	TNONE,TB,TB,TS,
73 	TA,TB,TS,TB
74 } ;
75 
76 /*
77  * CEM port states
78  */
79 #define	CEM_PST_DOWN	0
80 #define	CEM_PST_UP	1
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) */
84 
85 /*
86  * all Globals  are defined in smc.h
87  * struct s_cfm
88  */
89 
90 /*
91  * function declarations
92  */
93 static void cfm_fsm(struct s_smc *smc, int cmd);
94 
95 /*
96 	init CFM state machine
97 	clear all CFM vars and flags
98 */
99 void cfm_init(struct s_smc *smc)
100 {
101 	smc->mib.fddiSMTCF_State = ACTIONS(SC0_ISOLATED) ;
102 	smc->r.rm_join = 0 ;
103 	smc->r.rm_loop = 0 ;
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 ;
108 }
109 
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)
115 {
116 
117 	switch (phy->mib->fddiPORTMy_Type) {
118 	case TA:
119 		if ( !THRU_ENABLED(smc) && smc->y[PB].cf_join ) {
120 			phy->wc_flag = TRUE ;
121 		} else {
122 			phy->wc_flag = FALSE ;
123 		}
124 
125 		break;
126 	case TB:
127 		/* take precedence over PA */
128 		phy->wc_flag = FALSE ;
129 		break;
130 	case TS:
131 		phy->wc_flag = FALSE ;
132 		break;
133 	case TM:
134 		phy->wc_flag = FALSE ;
135 		break;
136 	}
137 
138 }
139 
140 void all_selection_criteria(struct s_smc *smc)
141 {
142 	struct s_phy	*phy ;
143 	int		p ;
144 
145 	for ( p = 0,phy = smc->y ; p < NUMPHYS; p++, phy++ ) {
146 		/* Do the selection criteria */
147 		selection_criteria (smc,phy);
148 	}
149 }
150 
151 static void cem_priv_state(struct s_smc *smc, int event)
152 /* State machine for private PORT states: used to optimize dual homing */
153 {
154 	int	np;	/* Number of the port */
155 	int	i;
156 
157 	/* Do this only in a DAS */
158 	if (smc->s.sas != SMT_DAS )
159 		return ;
160 
161 	np = event - CF_JOIN;
162 
163 	if (np != PA && np != PB) {
164 		return ;
165 	}
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 ;
172 	}
173 
174 	/* Don't set an hold port to down */
175 
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) ;
182 		}
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) ;
186 		}
187 		if ( smc->y[i].cem_pst == CEM_PST_DOWN && smc->y[i].wc_flag ) {
188 			/*
189 			 * The port must be restarted when the wc_flag
190 			 * will be reset. So set the port on hold.
191 			 */
192 			smc->y[i].cem_pst = CEM_PST_HOLD;
193 		}
194 	}
195 	return ;
196 }
197 
198 /*
199 	CFM state machine
200 	called by dispatcher
201 
202 	do
203 		display state change
204 		process event
205 	until SM is stable
206 */
207 void cfm(struct s_smc *smc, int event)
208 {
209 	int	state ;		/* remember last state */
210 	int	cond ;
211 	int	oldstate ;
212 
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 */
218 
219 	all_selection_criteria (smc);
220 
221 	/* We will check now whether a state transition is allowed or not */
222 	/*  - change the portstates */
223 	cem_priv_state (smc, event);
224 
225 	oldstate = smc->mib.fddiSMTCF_State ;
226 	do {
227 		DB_CFM("CFM : state %s%s event %s",
228 		       smc->mib.fddiSMTCF_State & AFLAG ? "ACTIONS " : "",
229 		       cfm_states[smc->mib.fddiSMTCF_State & ~AFLAG],
230 		       cfm_events[event]);
231 		state = smc->mib.fddiSMTCF_State ;
232 		cfm_fsm(smc,event) ;
233 		event = 0 ;
234 	} while (state != smc->mib.fddiSMTCF_State) ;
235 
236 #ifndef	SLIM_SMT
237 	/*
238 	 * check peer wrap condition
239 	 */
240 	cond = FALSE ;
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 ) ) {
248 			cond = TRUE ;
249 	}
250 	if (cond != smc->mib.fddiSMTPeerWrapFlag)
251 		smt_srf_event(smc,SMT_COND_SMT_PEER_WRAP,0,cond) ;
252 
253 #if	0
254 	/*
255 	 * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired
256 	 * to the primary path.
257 	 */
258 	/*
259 	 * path change
260 	 */
261 	if (smc->mib.fddiSMTCF_State != oldstate) {
262 		smt_srf_event(smc,SMT_EVENT_MAC_PATH_CHANGE,INDEX_MAC,0) ;
263 	}
264 #endif
265 #endif	/* no SLIM_SMT */
266 
267 	/*
268 	 * set MAC port type
269 	 */
270 	smc->mib.m[MAC0].fddiMACDownstreamPORTType =
271 		cf_to_ptype[smc->mib.fddiSMTCF_State] ;
272 	cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ;
273 }
274 
275 /*
276 	process CFM event
277 */
278 /*ARGSUSED1*/
279 static void cfm_fsm(struct s_smc *smc, int cmd)
280 {
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 */
293 		ACTIONS_DONE() ;
294 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
295 		break;
296 	case SC0_ISOLATED :
297 		/*SC07*/
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) ;
302 			break ;
303 		}
304 		/*SC01*/
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) ;
308 			break ;
309 		}
310 		/*SC02*/
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) ;
314 			break ;
315 		}
316 		break ;
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 */
328 		}
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 */
333 		}
334 		ACTIONS_DONE() ;
335 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
336 		break ;
337 	case SC9_C_WRAP_A :
338 		/*SC10*/
339 		if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) &&
340 		      !smc->y[PA].cf_loop ) {
341 			GO_STATE(SC0_ISOLATED) ;
342 			break ;
343 		}
344 		/*SC12*/
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) ;
354 			break ;
355 		}
356 		/*SC14*/
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) ;
366 			break ;
367 		}
368 		/*SC15*/
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) ;
379 			break ;
380 		}
381 		break ;
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 */
393 		}
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 */
398 		}
399 		ACTIONS_DONE() ;
400 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
401 		break ;
402 	case SC10_C_WRAP_B :
403 		/*SC20*/
404 		if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) {
405 			GO_STATE(SC0_ISOLATED) ;
406 			break ;
407 		}
408 		/*SC21*/
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) ;
413 			break ;
414 		}
415 		/*SC24*/
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) ;
422 			break ;
423 		}
424 		/*SC25*/
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) ;
431 			break ;
432 		}
433 		break ;
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 */
444 		ACTIONS_DONE() ;
445 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
446 		break ;
447 	case SC4_THRU_A :
448 		/*SC41*/
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) ;
452 			break ;
453 		}
454 		/*SC42*/
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) ;
458 			break ;
459 		}
460 		/*SC45*/
461 		else if (smc->s.attach_s) {
462 			smc->y[PB].scrub = TRUE ;
463 			GO_STATE(SC5_THRU_B) ;
464 			break ;
465 		}
466 		break ;
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 */
477 		ACTIONS_DONE() ;
478 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
479 		break ;
480 	case SC5_THRU_B :
481 		/*SC51*/
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) ;
485 			break ;
486 		}
487 		/*SC52*/
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) ;
491 			break ;
492 		}
493 		/*SC54*/
494 		else if (!smc->s.attach_s) {
495 			smc->y[PA].scrub = TRUE ;
496 			GO_STATE(SC4_THRU_A) ;
497 			break ;
498 		}
499 		break ;
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 */
509 		}
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 */
514 		}
515 		ACTIONS_DONE() ;
516 		DB_CFMN(1, "CFM : %s", cfm_states[smc->mib.fddiSMTCF_State]);
517 		break ;
518 	case SC11_C_WRAP_S :
519 		/*SC70*/
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) ;
523 			break ;
524 		}
525 		break ;
526 	default:
527 		SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ;
528 		break;
529 	}
530 }
531 
532 /*
533  * get MAC's input Port
534  *	return :
535  *		PA or PB
536  */
537 int cfm_get_mac_input(struct s_smc *smc)
538 {
539 	return (smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
540 		smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA;
541 }
542 
543 /*
544  * get MAC's output Port
545  *	return :
546  *		PA or PB
547  */
548 int cfm_get_mac_output(struct s_smc *smc)
549 {
550 	return (smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
551 		smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA;
552 }
553 
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
558 } ;
559 
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
564 } ;
565 
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
570 } ;
571 
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
576 } ;
577 
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,
581 } ;
582 
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,
586 } ;
587 
588 int cem_build_path(struct s_smc *smc, char *to, int path_index)
589 {
590 	char	*path ;
591 	int	len ;
592 
593 	switch (smc->mib.fddiSMTCF_State) {
594 	default :
595 	case SC0_ISOLATED :
596 		path = smc->s.sas ? path_iso_s : path_iso ;
597 		len = smc->s.sas ? sizeof(path_iso_s) :  sizeof(path_iso) ;
598 		break ;
599 	case SC9_C_WRAP_A :
600 		path = path_wrap_a ;
601 		len = sizeof(path_wrap_a) ;
602 		break ;
603 	case SC10_C_WRAP_B :
604 		path = path_wrap_b ;
605 		len = sizeof(path_wrap_b) ;
606 		break ;
607 	case SC4_THRU_A :
608 		path = path_thru ;
609 		len = sizeof(path_thru) ;
610 		break ;
611 	case SC11_C_WRAP_S :
612 		path = path_wrap_s ;
613 		len = sizeof(path_wrap_s) ;
614 		break ;
615 	}
616 	memcpy(to,path,len) ;
617 
618 	LINT_USE(path_index);
619 
620 	return len;
621 }
622