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