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#if	0
260	/*
261	 * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired
262	 * to the primary path.
263	 */
264	/*
265	 * path change
266	 */
267	if (smc->mib.fddiSMTCF_State != oldstate) {
268		smt_srf_event(smc,SMT_EVENT_MAC_PATH_CHANGE,INDEX_MAC,0) ;
269	}
270#endif
271#endif	/* no SLIM_SMT */
272
273	/*
274	 * set MAC port type
275	 */
276	smc->mib.m[MAC0].fddiMACDownstreamPORTType =
277		cf_to_ptype[smc->mib.fddiSMTCF_State] ;
278	cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ;
279}
280
281/*
282	process CFM event
283*/
284/*ARGSUSED1*/
285static void cfm_fsm(struct s_smc *smc, int cmd)
286{
287	switch(smc->mib.fddiSMTCF_State) {
288	case ACTIONS(SC0_ISOLATED) :
289		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
290		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
291		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
292		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
293		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ;
294		config_mux(smc,MUX_ISOLATE) ;	/* configure PHY Mux */
295		smc->r.rm_loop = FALSE ;
296		smc->r.rm_join = FALSE ;
297		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
298		/* Don't do the WC-Flag changing here */
299		ACTIONS_DONE() ;
300		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
301		break;
302	case SC0_ISOLATED :
303		/*SC07*/
304		/*SAS port can be PA or PB ! */
305		if (smc->s.sas && (smc->y[PA].cf_join || smc->y[PA].cf_loop ||
306				smc->y[PB].cf_join || smc->y[PB].cf_loop)) {
307			GO_STATE(SC11_C_WRAP_S) ;
308			break ;
309		}
310		/*SC01*/
311		if ((smc->y[PA].cem_pst == CEM_PST_UP && smc->y[PA].cf_join &&
312		     !smc->y[PA].wc_flag) || smc->y[PA].cf_loop) {
313			GO_STATE(SC9_C_WRAP_A) ;
314			break ;
315		}
316		/*SC02*/
317		if ((smc->y[PB].cem_pst == CEM_PST_UP && smc->y[PB].cf_join &&
318		     !smc->y[PB].wc_flag) || smc->y[PB].cf_loop) {
319			GO_STATE(SC10_C_WRAP_B) ;
320			break ;
321		}
322		break ;
323	case ACTIONS(SC9_C_WRAP_A) :
324		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
325		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
326		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
327		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
328		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
329		config_mux(smc,MUX_WRAPA) ;		/* configure PHY mux */
330		if (smc->y[PA].cf_loop) {
331			smc->r.rm_join = FALSE ;
332			smc->r.rm_loop = TRUE ;
333			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
334		}
335		if (smc->y[PA].cf_join) {
336			smc->r.rm_loop = FALSE ;
337			smc->r.rm_join = TRUE ;
338			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
339		}
340		ACTIONS_DONE() ;
341		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
342		break ;
343	case SC9_C_WRAP_A :
344		/*SC10*/
345		if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) &&
346		      !smc->y[PA].cf_loop ) {
347			GO_STATE(SC0_ISOLATED) ;
348			break ;
349		}
350		/*SC12*/
351		else if ( (smc->y[PB].cf_loop && smc->y[PA].cf_join &&
352			   smc->y[PA].cem_pst == CEM_PST_UP) ||
353			  ((smc->y[PB].cf_loop ||
354			   (smc->y[PB].cf_join &&
355			    smc->y[PB].cem_pst == CEM_PST_UP)) &&
356			    (smc->y[PA].pc_mode == PM_TREE ||
357			     smc->y[PB].pc_mode == PM_TREE))) {
358			smc->y[PA].scrub = TRUE ;
359			GO_STATE(SC10_C_WRAP_B) ;
360			break ;
361		}
362		/*SC14*/
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 && smc->y[PB].cf_join &&
367			  smc->y[PB].cem_pst == CEM_PST_UP &&
368			  smc->y[PB].pc_mode == PM_PEER) {
369			smc->y[PA].scrub = TRUE ;
370			smc->y[PB].scrub = TRUE ;
371			GO_STATE(SC4_THRU_A) ;
372			break ;
373		}
374		/*SC15*/
375		else if ( smc->s.attach_s &&
376			  smc->y[PA].cf_join &&
377			  smc->y[PA].cem_pst == CEM_PST_UP &&
378			  smc->y[PA].pc_mode == PM_PEER &&
379			  smc->y[PB].cf_join &&
380			  smc->y[PB].cem_pst == CEM_PST_UP &&
381			  smc->y[PB].pc_mode == PM_PEER) {
382			smc->y[PA].scrub = TRUE ;
383			smc->y[PB].scrub = TRUE ;
384			GO_STATE(SC5_THRU_B) ;
385			break ;
386		}
387		break ;
388	case ACTIONS(SC10_C_WRAP_B) :
389		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
390		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
391		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
392		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
393		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
394		config_mux(smc,MUX_WRAPB) ;		/* configure PHY mux */
395		if (smc->y[PB].cf_loop) {
396			smc->r.rm_join = FALSE ;
397			smc->r.rm_loop = TRUE ;
398			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
399		}
400		if (smc->y[PB].cf_join) {
401			smc->r.rm_loop = FALSE ;
402			smc->r.rm_join = TRUE ;
403			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
404		}
405		ACTIONS_DONE() ;
406		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
407		break ;
408	case SC10_C_WRAP_B :
409		/*SC20*/
410		if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) {
411			GO_STATE(SC0_ISOLATED) ;
412			break ;
413		}
414		/*SC21*/
415		else if ( smc->y[PA].cf_loop && smc->y[PA].pc_mode == PM_PEER &&
416			  smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
417			smc->y[PB].scrub = TRUE ;
418			GO_STATE(SC9_C_WRAP_A) ;
419			break ;
420		}
421		/*SC24*/
422		else if (!smc->s.attach_s &&
423			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
424			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
425			smc->y[PA].scrub = TRUE ;
426			smc->y[PB].scrub = TRUE ;
427			GO_STATE(SC4_THRU_A) ;
428			break ;
429		}
430		/*SC25*/
431		else if ( smc->s.attach_s &&
432			 smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER &&
433			 smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) {
434			smc->y[PA].scrub = TRUE ;
435			smc->y[PB].scrub = TRUE ;
436			GO_STATE(SC5_THRU_B) ;
437			break ;
438		}
439		break ;
440	case ACTIONS(SC4_THRU_A) :
441		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
442		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
443		smc->mib.p[PA].fddiPORTMACPlacement = 0 ;
444		smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ;
445		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
446		config_mux(smc,MUX_THRUA) ;		/* configure PHY mux */
447		smc->r.rm_loop = FALSE ;
448		smc->r.rm_join = TRUE ;
449		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
450		ACTIONS_DONE() ;
451		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
452		break ;
453	case SC4_THRU_A :
454		/*SC41*/
455		if (smc->y[PB].wc_flag || !smc->y[PB].cf_join) {
456			smc->y[PA].scrub = TRUE ;
457			GO_STATE(SC9_C_WRAP_A) ;
458			break ;
459		}
460		/*SC42*/
461		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
462			smc->y[PB].scrub = TRUE ;
463			GO_STATE(SC10_C_WRAP_B) ;
464			break ;
465		}
466		/*SC45*/
467		else if (smc->s.attach_s) {
468			smc->y[PB].scrub = TRUE ;
469			GO_STATE(SC5_THRU_B) ;
470			break ;
471		}
472		break ;
473	case ACTIONS(SC5_THRU_B) :
474		smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ;
475		smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ;
476		smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ;
477		smc->mib.p[PB].fddiPORTMACPlacement = 0 ;
478		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ;
479		config_mux(smc,MUX_THRUB) ;		/* configure PHY mux */
480		smc->r.rm_loop = FALSE ;
481		smc->r.rm_join = TRUE ;
482		queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
483		ACTIONS_DONE() ;
484		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
485		break ;
486	case SC5_THRU_B :
487		/*SC51*/
488		if (!smc->y[PB].cf_join || smc->y[PB].wc_flag) {
489			smc->y[PA].scrub = TRUE ;
490			GO_STATE(SC9_C_WRAP_A) ;
491			break ;
492		}
493		/*SC52*/
494		else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) {
495			smc->y[PB].scrub = TRUE ;
496			GO_STATE(SC10_C_WRAP_B) ;
497			break ;
498		}
499		/*SC54*/
500		else if (!smc->s.attach_s) {
501			smc->y[PA].scrub = TRUE ;
502			GO_STATE(SC4_THRU_A) ;
503			break ;
504		}
505		break ;
506	case ACTIONS(SC11_C_WRAP_S) :
507		smc->mib.p[PS].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ;
508		smc->mib.p[PS].fddiPORTMACPlacement = INDEX_MAC ;
509		smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ;
510		config_mux(smc,MUX_WRAPS) ;		/* configure PHY mux */
511		if (smc->y[PA].cf_loop || smc->y[PB].cf_loop) {
512			smc->r.rm_join = FALSE ;
513			smc->r.rm_loop = TRUE ;
514			queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */
515		}
516		if (smc->y[PA].cf_join || smc->y[PB].cf_join) {
517			smc->r.rm_loop = FALSE ;
518			smc->r.rm_join = TRUE ;
519			queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */
520		}
521		ACTIONS_DONE() ;
522		DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ;
523		break ;
524	case SC11_C_WRAP_S :
525		/*SC70*/
526		if ( !smc->y[PA].cf_join && !smc->y[PA].cf_loop &&
527		     !smc->y[PB].cf_join && !smc->y[PB].cf_loop) {
528			GO_STATE(SC0_ISOLATED) ;
529			break ;
530		}
531		break ;
532	default:
533		SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ;
534		break;
535	}
536}
537
538/*
539 * get MAC's input Port
540 *	return :
541 *		PA or PB
542 */
543int cfm_get_mac_input(struct s_smc *smc)
544{
545	return (smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
546		smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA;
547}
548
549/*
550 * get MAC's output Port
551 *	return :
552 *		PA or PB
553 */
554int cfm_get_mac_output(struct s_smc *smc)
555{
556	return (smc->mib.fddiSMTCF_State == SC10_C_WRAP_B ||
557		smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA;
558}
559
560static char path_iso[] = {
561	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO,
562	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
563	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_ISO
564} ;
565
566static char path_wrap_a[] = {
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_ISO
570} ;
571
572static char path_wrap_b[] = {
573	0,0,	0,RES_PORT,	0,PB + INDEX_PORT,	0,PATH_PRIM,
574	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
575	0,0,	0,RES_PORT,	0,PA + INDEX_PORT,	0,PATH_ISO
576} ;
577
578static char path_thru[] = {
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_PRIM
582} ;
583
584static char path_wrap_s[] = {
585	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_PRIM,
586	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_PRIM,
587} ;
588
589static char path_iso_s[] = {
590	0,0,	0,RES_PORT,	0,PS + INDEX_PORT,	0,PATH_ISO,
591	0,0,	0,RES_MAC,	0,INDEX_MAC,		0,PATH_ISO,
592} ;
593
594int cem_build_path(struct s_smc *smc, char *to, int path_index)
595{
596	char	*path ;
597	int	len ;
598
599	switch (smc->mib.fddiSMTCF_State) {
600	default :
601	case SC0_ISOLATED :
602		path = smc->s.sas ? path_iso_s : path_iso ;
603		len = smc->s.sas ? sizeof(path_iso_s) :  sizeof(path_iso) ;
604		break ;
605	case SC9_C_WRAP_A :
606		path = path_wrap_a ;
607		len = sizeof(path_wrap_a) ;
608		break ;
609	case SC10_C_WRAP_B :
610		path = path_wrap_b ;
611		len = sizeof(path_wrap_b) ;
612		break ;
613	case SC4_THRU_A :
614		path = path_thru ;
615		len = sizeof(path_thru) ;
616		break ;
617	case SC11_C_WRAP_S :
618		path = path_wrap_s ;
619		len = sizeof(path_wrap_s) ;
620		break ;
621	}
622	memcpy(to,path,len) ;
623
624	LINT_USE(path_index);
625
626	return len;
627}
628