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 independent state machine implemantation
25 *	The following external SMT functions are referenced :
26 *
27 *		queue_event()
28 *
29 *	The following external HW dependent functions are referenced :
30 *		config_mux()
31 *
32 *	The following HW dependent 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
44static 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 */
59static 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 */
68static 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 */
76static const unsigned 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 */
99static void cfm_fsm(struct s_smc *smc, int cmd);
100
101/*
102	init CFM state machine
103	clear all CFM vars and flags
104*/
105void cfm_init(struct s_smc *smc)
106{
107	smc->mib.fddiSMTCF_State = ACTIONS(SC0_ISOLATED) ;
108	smc->r.rm_join = 0 ;
109	smc->r.rm_loop = 0 ;
110	smc->y[PA].scrub = 0 ;
111	smc->y[PB].scrub = 0 ;
112	smc->y[PA].cem_pst = CEM_PST_DOWN ;
113	smc->y[PB].cem_pst = CEM_PST_DOWN ;
114}
115
116/* Some terms conditions used by the selection criteria */
117#define THRU_ENABLED(smc)	(smc->y[PA].pc_mode != PM_TREE && \
118				 smc->y[PB].pc_mode != PM_TREE)
119/* Selection criteria for the ports */
120static void selection_criteria (struct s_smc *smc, struct s_phy *phy)
121{
122
123	switch (phy->mib->fddiPORTMy_Type) {
124	case TA:
125		if ( !THRU_ENABLED(smc) && smc->y[PB].cf_join ) {
126			phy->wc_flag = TRUE ;
127		} else {
128			phy->wc_flag = FALSE ;
129		}
130
131		break;
132	case TB:
133		/* take precedence over PA */
134		phy->wc_flag = FALSE ;
135		break;
136	case TS:
137		phy->wc_flag = FALSE ;
138		break;
139	case TM:
140		phy->wc_flag = FALSE ;
141		break;
142	}
143
144}
145
146void all_selection_criteria(struct s_smc *smc)
147{
148	struct s_phy	*phy ;
149	int		p ;
150
151	for ( p = 0,phy = smc->y ; p < NUMPHYS; p++, phy++ ) {
152		/* Do the selection criteria */
153		selection_criteria (smc,phy);
154	}
155}
156
157static void cem_priv_state(struct s_smc *smc, int event)
158/* State machine for private PORT states: used to optimize dual homing */
159{
160	int	np;	/* Number of the port */
161	int	i;
162
163	/* Do this only in a DAS */
164	if (smc->s.sas != SMT_DAS )
165		return ;
166
167	np = event - CF_JOIN;
168
169	if (np != PA && np != PB) {
170		return ;
171	}
172	/* Change the port state according to the event (portnumber) */
173	if (smc->y[np].cf_join) {
174		smc->y[np].cem_pst = CEM_PST_UP ;
175	} else if (!smc->y[np].wc_flag) {
176		/* set the port to done only if it is not withheld */
177		smc->y[np].cem_pst = CEM_PST_DOWN ;
178	}
179
180	/* Don't set an hold port to down */
181
182	/* Check all ports of restart conditions */
183	for (i = 0 ; i < 2 ; i ++ ) {
184		/* Check all port for PORT is on hold and no withhold is done */
185		if ( smc->y[i].cem_pst == CEM_PST_HOLD && !smc->y[i].wc_flag ) {
186			smc->y[i].cem_pst = CEM_PST_DOWN;
187			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
188		}
189		if ( smc->y[i].cem_pst == CEM_PST_UP && smc->y[i].wc_flag ) {
190			smc->y[i].cem_pst = CEM_PST_HOLD;
191			queue_event(smc,(int)(EVENT_PCM+i),PC_START) ;
192		}
193		if ( smc->y[i].cem_pst == CEM_PST_DOWN && smc->y[i].wc_flag ) {
194			/*
195			 * The port must be restarted when the wc_flag
196			 * will be reset. So set the port on hold.
197			 */
198			smc->y[i].cem_pst = CEM_PST_HOLD;
199		}
200	}
201	return ;
202}
203
204/*
205	CFM state machine
206	called by dispatcher
207
208	do
209		display state change
210		process event
211	until SM is stable
212*/
213void cfm(struct s_smc *smc, int event)
214{
215	int	state ;		/* remember last state */
216	int	cond ;
217	int	oldstate ;
218
219	/* We will do the following: */
220	/*  - compute the variable WC_Flag for every port (This is where */
221	/*    we can extend the requested path checking !!) */
222	/*  - do the old (SMT 6.2 like) state machine */
223	/*  - do the resulting station states */
224
225	all_selection_criteria (smc);
226
227	/* We will check now whether a state transition is allowed or not */
228	/*  - change the portstates */
229	cem_priv_state (smc, event);
230
231	oldstate = smc->mib.fddiSMTCF_State ;
232	do {
233		DB_CFM("CFM : state %s%s",
234			(smc->mib.fddiSMTCF_State & AFLAG) ? "ACTIONS " : "",
235			cfm_states[smc->mib.fddiSMTCF_State & ~AFLAG]) ;
236		DB_CFM(" event %s\n",cfm_events[event],0) ;
237		state = smc->mib.fddiSMTCF_State ;
238		cfm_fsm(smc,event) ;
239		event = 0 ;
240	} while (state != smc->mib.fddiSMTCF_State) ;
241
242#ifndef	SLIM_SMT
243	/*
244	 * check peer wrap condition
245	 */
246	cond = FALSE ;
247	if (	(smc->mib.fddiSMTCF_State == SC9_C_WRAP_A &&
248		smc->y[PA].pc_mode == PM_PEER) 	||
249		(smc->mib.fddiSMTCF_State == SC10_C_WRAP_B &&
250		smc->y[PB].pc_mode == PM_PEER) 	||
251		(smc->mib.fddiSMTCF_State == SC11_C_WRAP_S &&
252		smc->y[PS].pc_mode == PM_PEER &&
253		smc->y[PS].mib->fddiPORTNeighborType != TS ) ) {
254			cond = TRUE ;
255	}
256	if (cond != smc->mib.fddiSMTPeerWrapFlag)
257		smt_srf_event(smc,SMT_COND_SMT_PEER_WRAP,0,cond) ;
258
259#endif	/* no SLIM_SMT */
260
261	/*
262	 * set MAC port type
263	 */
264	smc->mib.m[MAC0].fddiMACDownstreamPORTType =
265		cf_to_ptype[smc->mib.fddiSMTCF_State] ;
266	cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ;
267}
268
269/*
270	process CFM event
271*/
272/*ARGSUSED1*/
273static void cfm_fsm(struct s_smc *smc, int cmd)
274{
275	switch(smc->mib.fddiSMTCF_State) {
276	case ACTIONS(SC0_ISOLATED) :
277		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
278		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
279		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
280		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
281		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ;
282		config_mux(smc,MUX_ISOLATE) ;	/* configure PHY Mux */
283		smc->r.rm_loop = FALSE ;
284		smc->r.rm_join = FALSE ;
285		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
286		/* Don't do the WC-Flag changing here */
287		ACTIONS_DONE() ;
288		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
289		break;
290	case SC0_ISOLATED :
291		/*SC07*/
292		/*SAS port can be PA or PB ! */
293		if (smc->s.sas && (smc->y[PA].cf_join || smc->y[PA].cf_loop ||
294				smc->y[PB].cf_join || smc->y[PB].cf_loop)) {
295			GO_STATE(SC11_C_WRAP_S) ;
296			break ;
297		}
298		/*SC01*/
299		if ((smc->y[PA].cem_pst == CEM_PST_UP && smc->y[PA].cf_join &&
300		     !smc->y[PA].wc_flag) || smc->y[PA].cf_loop) {
301			GO_STATE(SC9_C_WRAP_A) ;
302			break ;
303		}
304		/*SC02*/
305		if ((smc->y[PB].cem_pst == CEM_PST_UP && smc->y[PB].cf_join &&
306		     !smc->y[PB].wc_flag) || smc->y[PB].cf_loop) {
307			GO_STATE(SC10_C_WRAP_B) ;
308			break ;
309		}
310		break ;
311	case ACTIONS(SC9_C_WRAP_A) :
312		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
313		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
314		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
315		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
316		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
317		config_mux(smc,MUX_WRAPA) ;		/* configure PHY mux */
318		if (smc->y[PA].cf_loop) {
319			smc->r.rm_join = FALSE ;
320			smc->r.rm_loop = TRUE ;
321			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
322		}
323		if (smc->y[PA].cf_join) {
324			smc->r.rm_loop = FALSE ;
325			smc->r.rm_join = TRUE ;
326			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
327		}
328		ACTIONS_DONE() ;
329		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
330		break ;
331	case SC9_C_WRAP_A :
332		/*SC10*/
333		if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) &&
334		      !smc->y[PA].cf_loop ) {
335			GO_STATE(SC0_ISOLATED) ;
336			break ;
337		}
338		/*SC12*/
339		else if ( (smc->y[PB].cf_loop && smc->y[PA].cf_join &&
340			   smc->y[PA].cem_pst == CEM_PST_UP) ||
341			  ((smc->y[PB].cf_loop ||
342			   (smc->y[PB].cf_join &&
343			    smc->y[PB].cem_pst == CEM_PST_UP)) &&
344			    (smc->y[PA].pc_mode == PM_TREE ||
345			     smc->y[PB].pc_mode == PM_TREE))) {
346			smc->y[PA].scrub = TRUE ;
347			GO_STATE(SC10_C_WRAP_B) ;
348			break ;
349		}
350		/*SC14*/
351		else if (!smc->s.attach_s &&
352			  smc->y[PA].cf_join &&
353			  smc->y[PA].cem_pst == CEM_PST_UP &&
354			  smc->y[PA].pc_mode == PM_PEER && smc->y[PB].cf_join &&
355			  smc->y[PB].cem_pst == CEM_PST_UP &&
356			  smc->y[PB].pc_mode == PM_PEER) {
357			smc->y[PA].scrub = TRUE ;
358			smc->y[PB].scrub = TRUE ;
359			GO_STATE(SC4_THRU_A) ;
360			break ;
361		}
362		/*SC15*/
363		else if ( smc->s.attach_s &&
364			  smc->y[PA].cf_join &&
365			  smc->y[PA].cem_pst == CEM_PST_UP &&
366			  smc->y[PA].pc_mode == PM_PEER &&
367			  smc->y[PB].cf_join &&
368			  smc->y[PB].cem_pst == CEM_PST_UP &&
369			  smc->y[PB].pc_mode == PM_PEER) {
370			smc->y[PA].scrub = TRUE ;
371			smc->y[PB].scrub = TRUE ;
372			GO_STATE(SC5_THRU_B) ;
373			break ;
374		}
375		break ;
376	case ACTIONS(SC10_C_WRAP_B) :
377		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
378		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
379		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
380		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
381		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
382		config_mux(smc,MUX_WRAPB) ;		/* configure PHY mux */
383		if (smc->y[PB].cf_loop) {
384			smc->r.rm_join = FALSE ;
385			smc->r.rm_loop = TRUE ;
386			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
387		}
388		if (smc->y[PB].cf_join) {
389			smc->r.rm_loop = FALSE ;
390			smc->r.rm_join = TRUE ;
391			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
392		}
393		ACTIONS_DONE() ;
394		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
395		break ;
396	case SC10_C_WRAP_B :
397		/*SC20*/
398		if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) {
399			GO_STATE(SC0_ISOLATED) ;
400			break ;
401		}
402		/*SC21*/
403		else if ( smc->y[PA].cf_loop && smc->y[PA].pc_mode == PM_PEER &&
404			  smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
405			smc->y[PB].scrub = TRUE ;
406			GO_STATE(SC9_C_WRAP_A) ;
407			break ;
408		}
409		/*SC24*/
410		else if (!smc->s.attach_s &&
411			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
412			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
413			smc->y[PA].scrub = TRUE ;
414			smc->y[PB].scrub = TRUE ;
415			GO_STATE(SC4_THRU_A) ;
416			break ;
417		}
418		/*SC25*/
419		else if ( smc->s.attach_s &&
420			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
421			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
422			smc->y[PA].scrub = TRUE ;
423			smc->y[PB].scrub = TRUE ;
424			GO_STATE(SC5_THRU_B) ;
425			break ;
426		}
427		break ;
428	case ACTIONS(SC4_THRU_A) :
429		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
430		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
431		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
432		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
433		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
434		config_mux(smc,MUX_THRUA) ;		/* configure PHY mux */
435		smc->r.rm_loop = FALSE ;
436		smc->r.rm_join = TRUE ;
437		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
438		ACTIONS_DONE() ;
439		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
440		break ;
441	case SC4_THRU_A :
442		/*SC41*/
443		if (smc->y[PB].wc_flag || !smc->y[PB].cf_join) {
444			smc->y[PA].scrub = TRUE ;
445			GO_STATE(SC9_C_WRAP_A) ;
446			break ;
447		}
448		/*SC42*/
449		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
450			smc->y[PB].scrub = TRUE ;
451			GO_STATE(SC10_C_WRAP_B) ;
452			break ;
453		}
454		/*SC45*/
455		else if (smc->s.attach_s) {
456			smc->y[PB].scrub = TRUE ;
457			GO_STATE(SC5_THRU_B) ;
458			break ;
459		}
460		break ;
461	case ACTIONS(SC5_THRU_B) :
462		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
463		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
464		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
465		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
466		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
467		config_mux(smc,MUX_THRUB) ;		/* configure PHY mux */
468		smc->r.rm_loop = FALSE ;
469		smc->r.rm_join = TRUE ;
470		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
471		ACTIONS_DONE() ;
472		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
473		break ;
474	case SC5_THRU_B :
475		/*SC51*/
476		if (!smc->y[PB].cf_join || smc->y[PB].wc_flag) {
477			smc->y[PA].scrub = TRUE ;
478			GO_STATE(SC9_C_WRAP_A) ;
479			break ;
480		}
481		/*SC52*/
482		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
483			smc->y[PB].scrub = TRUE ;
484			GO_STATE(SC10_C_WRAP_B) ;
485			break ;
486		}
487		/*SC54*/
488		else if (!smc->s.attach_s) {
489			smc->y[PA].scrub = TRUE ;
490			GO_STATE(SC4_THRU_A) ;
491			break ;
492		}
493		break ;
494	case ACTIONS(SC11_C_WRAP_S) :
495		smc->mib.p[PS].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
496		smc->mib.p[PS].fddiPORTMACPlacement = INDEX_MAC ;
497		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
498		config_mux(smc,MUX_WRAPS) ;		/* configure PHY mux */
499		if (smc->y[PA].cf_loop || smc->y[PB].cf_loop) {
500			smc->r.rm_join = FALSE ;
501			smc->r.rm_loop = TRUE ;
502			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
503		}
504		if (smc->y[PA].cf_join || smc->y[PB].cf_join) {
505			smc->r.rm_loop = FALSE ;
506			smc->r.rm_join = TRUE ;
507			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
508		}
509		ACTIONS_DONE() ;
510		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
511		break ;
512	case SC11_C_WRAP_S :
513		/*SC70*/
514		if ( !smc->y[PA].cf_join && !smc->y[PA].cf_loop &&
515		     !smc->y[PB].cf_join && !smc->y[PB].cf_loop) {
516			GO_STATE(SC0_ISOLATED) ;
517			break ;
518		}
519		break ;
520	default:
521		SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ;
522		break;
523	}
524}
525
526/*
527 * get MAC's input Port
528 *	return :
529 *		PA or PB
530 */
531int cfm_get_mac_input(struct s_smc *smc)
532{
533	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
534		smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA) ;
535}
536
537/*
538 * get MAC's output Port
539 *	return :
540 *		PA or PB
541 */
542int cfm_get_mac_output(struct s_smc *smc)
543{
544	return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
545		smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA) ;
546}
547
548static char path_iso[] = {
549	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO,
550	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
551	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
552} ;
553
554static char path_wrap_a[] = {
555	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
556	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
557	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
558} ;
559
560static char path_wrap_b[] = {
561	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM,
562	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
563	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO
564} ;
565
566static char path_thru[] = {
567	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_PRIM,
568	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
569	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM
570} ;
571
572static char path_wrap_s[] = {
573	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_PRIM,
574	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
575} ;
576
577static char path_iso_s[] = {
578	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_ISO,
579	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
580} ;
581
582int cem_build_path(struct s_smc *smc, char *to, int path_index)
583{
584	char	*path ;
585	int	len ;
586
587	switch (smc->mib.fddiSMTCF_State) {
588	default :
589	case SC0_ISOLATED :
590		path = smc->s.sas ? path_iso_s : path_iso ;
591		len = smc->s.sas ? sizeof(path_iso_s) :  sizeof(path_iso) ;
592		break ;
593	case SC9_C_WRAP_A :
594		path = path_wrap_a ;
595		len = sizeof(path_wrap_a) ;
596		break ;
597	case SC10_C_WRAP_B :
598		path = path_wrap_b ;
599		len = sizeof(path_wrap_b) ;
600		break ;
601	case SC4_THRU_A :
602		path = path_thru ;
603		len = sizeof(path_thru) ;
604		break ;
605	case SC11_C_WRAP_S :
606		path = path_wrap_s ;
607		len = sizeof(path_wrap_s) ;
608		break ;
609	}
610	memcpy(to,path,len) ;
611
612	LINT_USE(path_index);
613
614	return(len) ;
615}
616