File: /usr/src/linux/drivers/net/skfp/cfm.c

1     /******************************************************************************
2      *
3      *	(C)Copyright 1998,1999 SysKonnect,
4      *	a business unit of Schneider & Koch & Co. Datensysteme GmbH.
5      *
6      *	See the file "skfddi.c" for further information.
7      *
8      *	This program is free software; you can redistribute it and/or modify
9      *	it under the terms of the GNU General Public License as published by
10      *	the Free Software Foundation; either version 2 of the License, or
11      *	(at your option) any later version.
12      *
13      *	The information in this file is provided "AS IS" without warranty.
14      *
15      ******************************************************************************/
16     
17     /*
18     	SMT CFM
19     	Configuration Management
20     	DAS with single MAC
21     */
22     
23     /*
24      *	Hardware independant state machine implemantation
25      *	The following external SMT functions are referenced :
26      *
27      *		queue_event()
28      *
29      *	The following external HW dependant functions are referenced :
30      *		config_mux()
31      *
32      *	The following HW dependant events are required :
33      *		NONE 
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     #ifndef	lint
44     static const char ID_sccs[] = "@(#)cfm.c	2.18 98/10/06 (C) SK " ;
45     #endif
46     
47     /*
48      * FSM Macros
49      */
50     #define AFLAG	0x10
51     #define GO_STATE(x)	(smc->mib.fddiSMTCF_State = (x)|AFLAG)
52     #define ACTIONS_DONE()	(smc->mib.fddiSMTCF_State &= ~AFLAG)
53     #define ACTIONS(x)	(x|AFLAG)
54     
55     #ifdef	DEBUG
56     /*
57      * symbolic state names
58      */
59     static const char * const cfm_states[] = {
60     	"SC0_ISOLATED","CF1","CF2","CF3","CF4",
61     	"SC1_WRAP_A","SC2_WRAP_B","SC5_TRHU_B","SC7_WRAP_S",
62     	"SC9_C_WRAP_A","SC10_C_WRAP_B","SC11_C_WRAP_S","SC4_THRU_A"
63     } ;
64     
65     /*
66      * symbolic event names
67      */
68     static const char * const cfm_events[] = {
69     	"NONE","CF_LOOP_A","CF_LOOP_B","CF_JOIN_A","CF_JOIN_B"
70     } ;
71     #endif
72     
73     /*
74      * map from state to downstream port type
75      */
76     static const u_char cf_to_ptype[] = {
77     	TNONE,TNONE,TNONE,TNONE,TNONE,
78     	TNONE,TB,TB,TS,
79     	TA,TB,TS,TB
80     } ;
81     
82     /*
83      * CEM port states
84      */
85     #define	CEM_PST_DOWN	0
86     #define	CEM_PST_UP	1
87     #define	CEM_PST_HOLD	2
88     /* define portstate array only for A and B port */
89     /* Do this within the smc structure (use in multiple cards) */
90     
91     /*
92      * all Globals  are defined in smc.h
93      * struct s_cfm
94      */
95     
96     /*
97      * function declarations
98      */
99     static void cfm_fsm() ;
100     
101     /*
102     	init CFM state machine
103     	clear all CFM vars and flags
104     */
105     void cfm_init(smc)
106     struct s_smc *smc ;
107     {
108     	smc->mib.fddiSMTCF_State = ACTIONS(SC0_ISOLATED) ;
109     	smc->r.rm_join = 0 ;
110     	smc->r.rm_loop = 0 ;
111     	smc->y[PA].scrub = 0 ;
112     	smc->y[PB].scrub = 0 ;
113     	smc->y[PA].cem_pst = CEM_PST_DOWN ;
114     	smc->y[PB].cem_pst = CEM_PST_DOWN ;
115     }
116     
117     /* Some terms conditions used by the selection criteria */
118     #define THRU_ENABLED(smc)	(smc->y[PA].pc_mode != PM_TREE && \
119     				 smc->y[PB].pc_mode != PM_TREE)
120     /* Selection criteria for the ports */
121     static void	selection_criteria (smc,phy)
122     struct s_smc	*smc ;
123     struct s_phy	*phy ;
124     {
125     
126     	switch (phy->mib->fddiPORTMy_Type) {
127     	case TA:
128     		if ( !THRU_ENABLED(smc) && smc->y[PB].cf_join ) {
129     			phy->wc_flag = TRUE ;
130     		} else {
131     			phy->wc_flag = FALSE ;
132     		}
133     
134     		break;
135     	case TB:
136     		/* take precedence over PA */
137     		phy->wc_flag = FALSE ;
138     		break;
139     	case TS:
140     		phy->wc_flag = FALSE ;
141     		break;
142     	case TM:
143     		phy->wc_flag = FALSE ;
144     		break;
145     	}
146     
147     }
148     
149     void	all_selection_criteria (smc)
150     struct s_smc *smc ;
151     {
152     	struct s_phy	*phy ;
153     	int		p ;
154     
155     	for ( p = 0,phy = smc->y ; p < NUMPHYS; p++, phy++ ) {
156     		/* Do the selection criteria */
157     		selection_criteria (smc,phy);
158     	}
159     }
160     
161     static void	cem_priv_state (smc, event)
162     struct s_smc *smc ;
163     int event ;
164     /* State machine for private PORT states: used to optimize dual homing */
165     {
166     	int	np;	/* Number of the port */
167     	int	i;
168     
169     	/* Do this only in a DAS */
170     	if (smc->s.sas != SMT_DAS )
171     		return ;
172     
173     	np = event - CF_JOIN;
174     
175     	if (np != PA && np != PB) {
176     		return ;
177     	}
178     	/* Change the port state according to the event (portnumber) */
179     	if (smc->y[np].cf_join) {
180     		smc->y[np].cem_pst = CEM_PST_UP ;
181     	} else if (!smc->y[np].wc_flag) {
182     		/* set the port to done only if it is not withheld */
183     		smc->y[np].cem_pst = CEM_PST_DOWN ;
184     	}
185     
186     	/* Don't set an hold port to down */
187     
188     	/* Check all ports of restart conditions */
189     	for (i = 0 ; i < 2 ; i ++ ) {
190     		/* Check all port for PORT is on hold and no withhold is done */
191     		if ( smc->y[i].cem_pst == CEM_PST_HOLD && !smc->y[i].wc_flag ) {
192     			smc->y[i].cem_pst = CEM_PST_DOWN;
193     			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
194     		}
195     		if ( smc->y[i].cem_pst == CEM_PST_UP && smc->y[i].wc_flag ) {
196     			smc->y[i].cem_pst = CEM_PST_HOLD;
197     			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
198     		}
199     		if ( smc->y[i].cem_pst == CEM_PST_DOWN && smc->y[i].wc_flag ) {
200     			/*
201     			 * The port must be restarted when the wc_flag
202     			 * will be reset. So set the port on hold.
203     			 */
204     			smc->y[i].cem_pst = CEM_PST_HOLD;
205     		}
206     	}
207     	return ;
208     }
209     
210     /*
211     	CFM state machine
212     	called by dispatcher
213     
214     	do
215     		display state change
216     		process event
217     	until SM is stable
218     */
219     void cfm(smc,event)
220     struct s_smc *smc ;
221     int event ;
222     {
223     	int	state ;		/* remember last state */
224     	int	cond ;
225     	int	oldstate ;
226     
227     	/* We will do the following: */
228     	/*  - compute the variable WC_Flag for every port (This is where */
229     	/*    we can extend the requested path checking !!) */
230     	/*  - do the old (SMT 6.2 like) state machine */
231     	/*  - do the resulting station states */
232     
233     	all_selection_criteria (smc);
234     
235     	/* We will check now whether a state transition is allowed or not */
236     	/*  - change the portstates */
237     	cem_priv_state (smc, event);
238     
239     	oldstate = smc->mib.fddiSMTCF_State ;
240     	do {
241     		DB_CFM("CFM : state %s%s",
242     			(smc->mib.fddiSMTCF_State & AFLAG) ? "ACTIONS " : "",
243     			cfm_states[smc->mib.fddiSMTCF_State & ~AFLAG]) ;
244     		DB_CFM(" event %s\n",cfm_events[event],0) ;
245     		state = smc->mib.fddiSMTCF_State ;
246     		cfm_fsm(smc,event) ;
247     		event = 0 ;
248     	} while (state != smc->mib.fddiSMTCF_State) ;
249     
250     #ifndef	SLIM_SMT
251     	/*
252     	 * check peer wrap condition
253     	 */
254     	cond = FALSE ;
255     	if (	(smc->mib.fddiSMTCF_State == SC9_C_WRAP_A &&
256     		smc->y[PA].pc_mode == PM_PEER) 	||
257     		(smc->mib.fddiSMTCF_State == SC10_C_WRAP_B &&
258     		smc->y[PB].pc_mode == PM_PEER) 	||
259     		(smc->mib.fddiSMTCF_State == SC11_C_WRAP_S &&
260     		smc->y[PS].pc_mode == PM_PEER &&
261     		smc->y[PS].mib->fddiPORTNeighborType != TS ) ) {
262     			cond = TRUE ;
263     	}
264     	if (cond != smc->mib.fddiSMTPeerWrapFlag)
265     		smt_srf_event(smc,SMT_COND_SMT_PEER_WRAP,0,cond) ;
266     
267     #if	0
268     	/*
269     	 * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired
270     	 * to the primary path.
271     	 */
272     	/*
273     	 * path change
274     	 */
275     	if (smc->mib.fddiSMTCF_State != oldstate) {
276     		smt_srf_event(smc,SMT_EVENT_MAC_PATH_CHANGE,INDEX_MAC,0) ;
277     	}
278     #endif
279     #endif	/* no SLIM_SMT */
280     
281     	/*
282     	 * set MAC port type
283     	 */
284     	smc->mib.m[MAC0].fddiMACDownstreamPORTType =
285     		cf_to_ptype[smc->mib.fddiSMTCF_State] ;
286     	cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ;
287     }
288     
289     /*
290     	process CFM event
291     */
292     /*ARGSUSED1*/
293     static void cfm_fsm(smc,cmd)
294     struct s_smc *smc ;
295     int cmd ;
296     {
297     	switch(smc->mib.fddiSMTCF_State) {
298     	case ACTIONS(SC0_ISOLATED) :
299     		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
300     		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
301     		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
302     		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
303     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ;
304     		config_mux(smc,MUX_ISOLATE) ;	/* configure PHY Mux */
305     		smc->r.rm_loop = FALSE ;
306     		smc->r.rm_join = FALSE ;
307     		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
308     		/* Don't do the WC-Flag changing here */
309     		ACTIONS_DONE() ;
310     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
311     		break;
312     	case SC0_ISOLATED :
313     		/*SC07*/
314     		/*SAS port can be PA or PB ! */
315     		if (smc->s.sas && (smc->y[PA].cf_join || smc->y[PA].cf_loop ||
316     				smc->y[PB].cf_join || smc->y[PB].cf_loop)) {
317     			GO_STATE(SC11_C_WRAP_S) ;
318     			break ;
319     		}
320     		/*SC01*/
321     		if ((smc->y[PA].cem_pst == CEM_PST_UP && smc->y[PA].cf_join &&
322     		     !smc->y[PA].wc_flag) || smc->y[PA].cf_loop) {
323     			GO_STATE(SC9_C_WRAP_A) ;
324     			break ;
325     		}
326     		/*SC02*/
327     		if ((smc->y[PB].cem_pst == CEM_PST_UP && smc->y[PB].cf_join &&
328     		     !smc->y[PB].wc_flag) || smc->y[PB].cf_loop) {
329     			GO_STATE(SC10_C_WRAP_B) ;
330     			break ;
331     		}
332     		break ;
333     	case ACTIONS(SC9_C_WRAP_A) :
334     		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
335     		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
336     		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
337     		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
338     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
339     		config_mux(smc,MUX_WRAPA) ;		/* configure PHY mux */
340     		if (smc->y[PA].cf_loop) {
341     			smc->r.rm_join = FALSE ;
342     			smc->r.rm_loop = TRUE ;
343     			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
344     		}
345     		if (smc->y[PA].cf_join) {
346     			smc->r.rm_loop = FALSE ;
347     			smc->r.rm_join = TRUE ;
348     			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
349     		}
350     		ACTIONS_DONE() ;
351     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
352     		break ;
353     	case SC9_C_WRAP_A :
354     		/*SC10*/
355     		if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) &&
356     		      !smc->y[PA].cf_loop ) {
357     			GO_STATE(SC0_ISOLATED) ;
358     			break ;
359     		}
360     		/*SC12*/
361     		else if ( (smc->y[PB].cf_loop && smc->y[PA].cf_join &&
362     			   smc->y[PA].cem_pst == CEM_PST_UP) ||
363     			  ((smc->y[PB].cf_loop ||
364     			   (smc->y[PB].cf_join &&
365     			    smc->y[PB].cem_pst == CEM_PST_UP)) &&
366     			    (smc->y[PA].pc_mode == PM_TREE ||
367     			     smc->y[PB].pc_mode == PM_TREE))) {
368     			smc->y[PA].scrub = TRUE ;
369     			GO_STATE(SC10_C_WRAP_B) ;
370     			break ;
371     		}
372     		/*SC14*/
373     		else if (!smc->s.attach_s &&
374     			  smc->y[PA].cf_join &&
375     			  smc->y[PA].cem_pst == CEM_PST_UP &&
376     			  smc->y[PA].pc_mode == PM_PEER && smc->y[PB].cf_join &&
377     			  smc->y[PB].cem_pst == CEM_PST_UP &&
378     			  smc->y[PB].pc_mode == PM_PEER) {
379     			smc->y[PA].scrub = TRUE ;
380     			smc->y[PB].scrub = TRUE ;
381     			GO_STATE(SC4_THRU_A) ;
382     			break ;
383     		}
384     		/*SC15*/
385     		else if ( smc->s.attach_s &&
386     			  smc->y[PA].cf_join &&
387     			  smc->y[PA].cem_pst == CEM_PST_UP &&
388     			  smc->y[PA].pc_mode == PM_PEER &&
389     			  smc->y[PB].cf_join &&
390     			  smc->y[PB].cem_pst == CEM_PST_UP &&
391     			  smc->y[PB].pc_mode == PM_PEER) {
392     			smc->y[PA].scrub = TRUE ;
393     			smc->y[PB].scrub = TRUE ;
394     			GO_STATE(SC5_THRU_B) ;
395     			break ;
396     		}
397     		break ;
398     	case ACTIONS(SC10_C_WRAP_B) :
399     		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
400     		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
401     		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
402     		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
403     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
404     		config_mux(smc,MUX_WRAPB) ;		/* configure PHY mux */
405     		if (smc->y[PB].cf_loop) {
406     			smc->r.rm_join = FALSE ;
407     			smc->r.rm_loop = TRUE ;
408     			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
409     		}
410     		if (smc->y[PB].cf_join) {
411     			smc->r.rm_loop = FALSE ;
412     			smc->r.rm_join = TRUE ;
413     			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
414     		}
415     		ACTIONS_DONE() ;
416     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
417     		break ;
418     	case SC10_C_WRAP_B :
419     		/*SC20*/
420     		if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) {
421     			GO_STATE(SC0_ISOLATED) ;
422     			break ;
423     		}
424     		/*SC21*/
425     		else if ( smc->y[PA].cf_loop && smc->y[PA].pc_mode == PM_PEER &&
426     			  smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
427     			smc->y[PB].scrub = TRUE ;
428     			GO_STATE(SC9_C_WRAP_A) ;
429     			break ;
430     		}
431     		/*SC24*/
432     		else if (!smc->s.attach_s &&
433     			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
434     			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
435     			smc->y[PA].scrub = TRUE ;
436     			smc->y[PB].scrub = TRUE ;
437     			GO_STATE(SC4_THRU_A) ;
438     			break ;
439     		}
440     		/*SC25*/
441     		else if ( smc->s.attach_s &&
442     			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
443     			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
444     			smc->y[PA].scrub = TRUE ;
445     			smc->y[PB].scrub = TRUE ;
446     			GO_STATE(SC5_THRU_B) ;
447     			break ;
448     		}
449     		break ;
450     	case ACTIONS(SC4_THRU_A) :
451     		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
452     		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
453     		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
454     		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
455     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
456     		config_mux(smc,MUX_THRUA) ;		/* configure PHY mux */
457     		smc->r.rm_loop = FALSE ;
458     		smc->r.rm_join = TRUE ;
459     		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
460     		ACTIONS_DONE() ;
461     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
462     		break ;
463     	case SC4_THRU_A :
464     		/*SC41*/
465     		if (smc->y[PB].wc_flag || !smc->y[PB].cf_join) {
466     			smc->y[PA].scrub = TRUE ;
467     			GO_STATE(SC9_C_WRAP_A) ;
468     			break ;
469     		}
470     		/*SC42*/
471     		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
472     			smc->y[PB].scrub = TRUE ;
473     			GO_STATE(SC10_C_WRAP_B) ;
474     			break ;
475     		}
476     		/*SC45*/
477     		else if (smc->s.attach_s) {
478     			smc->y[PB].scrub = TRUE ;
479     			GO_STATE(SC5_THRU_B) ;
480     			break ;
481     		}
482     		break ;
483     	case ACTIONS(SC5_THRU_B) :
484     		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
485     		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
486     		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
487     		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
488     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
489     		config_mux(smc,MUX_THRUB) ;		/* configure PHY mux */
490     		smc->r.rm_loop = FALSE ;
491     		smc->r.rm_join = TRUE ;
492     		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
493     		ACTIONS_DONE() ;
494     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
495     		break ;
496     	case SC5_THRU_B :
497     		/*SC51*/
498     		if (!smc->y[PB].cf_join || smc->y[PB].wc_flag) {
499     			smc->y[PA].scrub = TRUE ;
500     			GO_STATE(SC9_C_WRAP_A) ;
501     			break ;
502     		}
503     		/*SC52*/
504     		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
505     			smc->y[PB].scrub = TRUE ;
506     			GO_STATE(SC10_C_WRAP_B) ;
507     			break ;
508     		}
509     		/*SC54*/
510     		else if (!smc->s.attach_s) {
511     			smc->y[PA].scrub = TRUE ;
512     			GO_STATE(SC4_THRU_A) ;
513     			break ;
514     		}
515     		break ;
516     	case ACTIONS(SC11_C_WRAP_S) :
517     		smc->mib.p[PS].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
518     		smc->mib.p[PS].fddiPORTMACPlacement = INDEX_MAC ;
519     		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
520     		config_mux(smc,MUX_WRAPS) ;		/* configure PHY mux */
521     		if (smc->y[PA].cf_loop || smc->y[PB].cf_loop) {
522     			smc->r.rm_join = FALSE ;
523     			smc->r.rm_loop = TRUE ;
524     			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
525     		}
526     		if (smc->y[PA].cf_join || smc->y[PB].cf_join) {
527     			smc->r.rm_loop = FALSE ;
528     			smc->r.rm_join = TRUE ;
529     			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
530     		}
531     		ACTIONS_DONE() ;
532     		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
533     		break ;
534     	case SC11_C_WRAP_S :
535     		/*SC70*/
536     		if ( !smc->y[PA].cf_join && !smc->y[PA].cf_loop &&
537     		     !smc->y[PB].cf_join && !smc->y[PB].cf_loop) {
538     			GO_STATE(SC0_ISOLATED) ;
539     			break ;
540     		}
541     		break ;
542     	default:
543     		SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ;
544     		break;
545     	}
546     }
547     
548     /*
549      * get MAC's input Port
550      *	return :
551      *		PA or PB
552      */
553     int cfm_get_mac_input(smc)
554     struct s_smc *smc ;
555     {
556     	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
557     		smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA) ;
558     }
559     
560     /*
561      * get MAC's output Port
562      *	return :
563      *		PA or PB
564      */
565     int cfm_get_mac_output(smc)
566     struct s_smc *smc ;
567     {
568     	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
569     		smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA) ;
570     }
571     
572     static char path_iso[] = {
573     	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO,
574     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
575     	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
576     } ;
577     
578     static char path_wrap_a[] = {
579     	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
580     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
581     	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
582     } ;
583     
584     static char path_wrap_b[] = {
585     	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM,
586     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
587     	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO
588     } ;
589     
590     static char path_thru[] = {
591     	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
592     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
593     	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM
594     } ;
595     
596     static char path_wrap_s[] = {
597     	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_PRIM,
598     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
599     } ;
600     
601     static char path_iso_s[] = {
602     	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_ISO,
603     	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
604     } ;
605     
606     int cem_build_path(smc,to,path_index)
607     struct s_smc *smc ;
608     char *to ;
609     int path_index ;
610     {
611     	char	*path ;
612     	int	len ;
613     
614     	switch (smc->mib.fddiSMTCF_State) {
615     	default :
616     	case SC0_ISOLATED :
617     		path = smc->s.sas ? path_iso_s : path_iso ;
618     		len = smc->s.sas ? sizeof(path_iso_s) :  sizeof(path_iso) ;
619     		break ;
620     	case SC9_C_WRAP_A :
621     		path = path_wrap_a ;
622     		len = sizeof(path_wrap_a) ;
623     		break ;
624     	case SC10_C_WRAP_B :
625     		path = path_wrap_b ;
626     		len = sizeof(path_wrap_b) ;
627     		break ;
628     	case SC4_THRU_A :
629     		path = path_thru ;
630     		len = sizeof(path_thru) ;
631     		break ;
632     	case SC11_C_WRAP_S :
633     		path = path_wrap_s ;
634     		len = sizeof(path_wrap_s) ;
635     		break ;
636     	}
637     	memcpy(to,path,len) ;
638     
639     	LINT_USE(path_index);
640     
641     	return(len) ;
642     }
643