Back to home page

LXR

 
 

    


0001 /******************************************************************************
0002  *
0003  *  (C)Copyright 1998,1999 SysKonnect,
0004  *  a business unit of Schneider & Koch & Co. Datensysteme GmbH.
0005  *
0006  *  See the file "skfddi.c" for further information.
0007  *
0008  *  This program is free software; you can redistribute it and/or modify
0009  *  it under the terms of the GNU General Public License as published by
0010  *  the Free Software Foundation; either version 2 of the License, or
0011  *  (at your option) any later version.
0012  *
0013  *  The information in this file is provided "AS IS" without warranty.
0014  *
0015  ******************************************************************************/
0016 
0017 /*
0018     PCM
0019     Physical Connection Management
0020 */
0021 
0022 /*
0023  * Hardware independent state machine implemantation
0024  * The following external SMT functions are referenced :
0025  *
0026  *      queue_event()
0027  *      smt_timer_start()
0028  *      smt_timer_stop()
0029  *
0030  *  The following external HW dependent functions are referenced :
0031  *      sm_pm_control()
0032  *      sm_ph_linestate()
0033  *      sm_pm_ls_latch()
0034  *
0035  *  The following HW dependent events are required :
0036  *      PC_QLS
0037  *      PC_ILS
0038  *      PC_HLS
0039  *      PC_MLS
0040  *      PC_NSE
0041  *      PC_LEM
0042  *
0043  */
0044 
0045 
0046 #include "h/types.h"
0047 #include "h/fddi.h"
0048 #include "h/smc.h"
0049 #include "h/supern_2.h"
0050 #define KERNEL
0051 #include "h/smtstate.h"
0052 
0053 #ifndef lint
0054 static const char ID_sccs[] = "@(#)pcmplc.c 2.55 99/08/05 (C) SK " ;
0055 #endif
0056 
0057 #ifdef  FDDI_MIB
0058 extern int snmp_fddi_trap(
0059 #ifdef  ANSIC
0060 struct s_smc    * smc, int  type, int  index
0061 #endif
0062 );
0063 #endif
0064 #ifdef  CONCENTRATOR
0065 extern int plc_is_installed(
0066 #ifdef  ANSIC
0067 struct s_smc *smc ,
0068 int p
0069 #endif
0070 ) ;
0071 #endif
0072 /*
0073  * FSM Macros
0074  */
0075 #define AFLAG       (0x20)
0076 #define GO_STATE(x) (mib->fddiPORTPCMState = (x)|AFLAG)
0077 #define ACTIONS_DONE()  (mib->fddiPORTPCMState &= ~AFLAG)
0078 #define ACTIONS(x)  (x|AFLAG)
0079 
0080 /*
0081  * PCM states
0082  */
0083 #define PC0_OFF         0
0084 #define PC1_BREAK       1
0085 #define PC2_TRACE       2
0086 #define PC3_CONNECT     3
0087 #define PC4_NEXT        4
0088 #define PC5_SIGNAL      5
0089 #define PC6_JOIN        6
0090 #define PC7_VERIFY      7
0091 #define PC8_ACTIVE      8
0092 #define PC9_MAINT       9
0093 
0094 #ifdef  DEBUG
0095 /*
0096  * symbolic state names
0097  */
0098 static const char * const pcm_states[] =  {
0099     "PC0_OFF","PC1_BREAK","PC2_TRACE","PC3_CONNECT","PC4_NEXT",
0100     "PC5_SIGNAL","PC6_JOIN","PC7_VERIFY","PC8_ACTIVE","PC9_MAINT"
0101 } ;
0102 
0103 /*
0104  * symbolic event names
0105  */
0106 static const char * const pcm_events[] = {
0107     "NONE","PC_START","PC_STOP","PC_LOOP","PC_JOIN","PC_SIGNAL",
0108     "PC_REJECT","PC_MAINT","PC_TRACE","PC_PDR",
0109     "PC_ENABLE","PC_DISABLE",
0110     "PC_QLS","PC_ILS","PC_MLS","PC_HLS","PC_LS_PDR","PC_LS_NONE",
0111     "PC_TIMEOUT_TB_MAX","PC_TIMEOUT_TB_MIN",
0112     "PC_TIMEOUT_C_MIN","PC_TIMEOUT_T_OUT",
0113     "PC_TIMEOUT_TL_MIN","PC_TIMEOUT_T_NEXT","PC_TIMEOUT_LCT",
0114     "PC_NSE","PC_LEM"
0115 } ;
0116 #endif
0117 
0118 #ifdef  MOT_ELM
0119 /*
0120  * PCL-S control register
0121  * this register in the PLC-S controls the scrambling parameters
0122  */
0123 #define PLCS_CONTROL_C_U    0
0124 #define PLCS_CONTROL_C_S    (PL_C_SDOFF_ENABLE | PL_C_SDON_ENABLE | \
0125                  PL_C_CIPHER_ENABLE)
0126 #define PLCS_FASSERT_U      0
0127 #define PLCS_FASSERT_S      0xFd76  /* 52.0 us */
0128 #define PLCS_FDEASSERT_U    0
0129 #define PLCS_FDEASSERT_S    0
0130 #else   /* nMOT_ELM */
0131 /*
0132  * PCL-S control register
0133  * this register in the PLC-S controls the scrambling parameters
0134  * can be patched for ANSI compliance if standard changes
0135  */
0136 static const u_char plcs_control_c_u[17] = "PLC_CNTRL_C_U=\0\0" ;
0137 static const u_char plcs_control_c_s[17] = "PLC_CNTRL_C_S=\01\02" ;
0138 
0139 #define PLCS_CONTROL_C_U (plcs_control_c_u[14] | (plcs_control_c_u[15]<<8))
0140 #define PLCS_CONTROL_C_S (plcs_control_c_s[14] | (plcs_control_c_s[15]<<8))
0141 #endif  /* nMOT_ELM */
0142 
0143 /*
0144  * external vars
0145  */
0146 /* struct definition see 'cmtdef.h' (also used by CFM) */
0147 
0148 #define PS_OFF      0
0149 #define PS_BIT3     1
0150 #define PS_BIT4     2
0151 #define PS_BIT7     3
0152 #define PS_LCT      4
0153 #define PS_BIT8     5
0154 #define PS_JOIN     6
0155 #define PS_ACTIVE   7
0156 
0157 #define LCT_LEM_MAX 255
0158 
0159 /*
0160  * PLC timing parameter
0161  */
0162 
0163 #define PLC_MS(m)   ((int)((0x10000L-(m*100000L/2048))))
0164 #define SLOW_TL_MIN PLC_MS(6)
0165 #define SLOW_C_MIN  PLC_MS(10)
0166 
0167 static  const struct plt {
0168     int timer ;         /* relative plc timer address */
0169     int para ;          /* default timing parameters */
0170 } pltm[] = {
0171     { PL_C_MIN, SLOW_C_MIN },   /* min t. to remain Connect State */
0172     { PL_TL_MIN, SLOW_TL_MIN }, /* min t. to transmit a Line State */
0173     { PL_TB_MIN, TP_TB_MIN },   /* min break time */
0174     { PL_T_OUT, TP_T_OUT },     /* Signaling timeout */
0175     { PL_LC_LENGTH, TP_LC_LENGTH }, /* Link Confidence Test Time */
0176     { PL_T_SCRUB, TP_T_SCRUB }, /* Scrub Time == MAC TVX time ! */
0177     { PL_NS_MAX, TP_NS_MAX },   /* max t. that noise is tolerated */
0178     { 0,0 }
0179 } ;
0180 
0181 /*
0182  * interrupt mask
0183  */
0184 #ifdef  SUPERNET_3
0185 /*
0186  * Do we need the EBUF error during signaling, too, to detect SUPERNET_3
0187  * PLL bug?
0188  */
0189 static const int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
0190             PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
0191 #else   /* SUPERNET_3 */
0192 /*
0193  * We do NOT need the elasticity buffer error during signaling.
0194  */
0195 static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
0196             PL_PCM_ENABLED | PL_SELF_TEST ;
0197 #endif  /* SUPERNET_3 */
0198 static const int plc_imsk_act = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
0199             PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
0200 
0201 /* internal functions */
0202 static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd);
0203 static void pc_rcode_actions(struct s_smc *smc, int bit, struct s_phy *phy);
0204 static void pc_tcode_actions(struct s_smc *smc, const int bit, struct s_phy *phy);
0205 static void reset_lem_struct(struct s_phy *phy);
0206 static void plc_init(struct s_smc *smc, int p);
0207 static void sm_ph_lem_start(struct s_smc *smc, int np, int threshold);
0208 static void sm_ph_lem_stop(struct s_smc *smc, int np);
0209 static void sm_ph_linestate(struct s_smc *smc, int phy, int ls);
0210 static void real_init_plc(struct s_smc *smc);
0211 
0212 /*
0213  * SMT timer interface
0214  *      start PCM timer 0
0215  */
0216 static void start_pcm_timer0(struct s_smc *smc, u_long value, int event,
0217                  struct s_phy *phy)
0218 {
0219     phy->timer0_exp = FALSE ;       /* clear timer event flag */
0220     smt_timer_start(smc,&phy->pcm_timer0,value,
0221         EV_TOKEN(EVENT_PCM+phy->np,event)) ;
0222 }
0223 /*
0224  * SMT timer interface
0225  *      stop PCM timer 0
0226  */
0227 static void stop_pcm_timer0(struct s_smc *smc, struct s_phy *phy)
0228 {
0229     if (phy->pcm_timer0.tm_active)
0230         smt_timer_stop(smc,&phy->pcm_timer0) ;
0231 }
0232 
0233 /*
0234     init PCM state machine (called by driver)
0235     clear all PCM vars and flags
0236 */
0237 void pcm_init(struct s_smc *smc)
0238 {
0239     int     i ;
0240     int     np ;
0241     struct s_phy    *phy ;
0242     struct fddi_mib_p   *mib ;
0243 
0244     for (np = 0,phy = smc->y ; np < NUMPHYS ; np++,phy++) {
0245         /* Indicates the type of PHY being used */
0246         mib = phy->mib ;
0247         mib->fddiPORTPCMState = ACTIONS(PC0_OFF) ;
0248         phy->np = np ;
0249         switch (smc->s.sas) {
0250 #ifdef  CONCENTRATOR
0251         case SMT_SAS :
0252             mib->fddiPORTMy_Type = (np == PS) ? TS : TM ;
0253             break ;
0254         case SMT_DAS :
0255             mib->fddiPORTMy_Type = (np == PA) ? TA :
0256                     (np == PB) ? TB : TM ;
0257             break ;
0258         case SMT_NAC :
0259             mib->fddiPORTMy_Type = TM ;
0260             break;
0261 #else
0262         case SMT_SAS :
0263             mib->fddiPORTMy_Type = (np == PS) ? TS : TNONE ;
0264             mib->fddiPORTHardwarePresent = (np == PS) ? TRUE :
0265                     FALSE ;
0266 #ifndef SUPERNET_3
0267             smc->y[PA].mib->fddiPORTPCMState = PC0_OFF ;
0268 #else
0269             smc->y[PB].mib->fddiPORTPCMState = PC0_OFF ;
0270 #endif
0271             break ;
0272         case SMT_DAS :
0273             mib->fddiPORTMy_Type = (np == PB) ? TB : TA ;
0274             break ;
0275 #endif
0276         }
0277         /*
0278          * set PMD-type
0279          */
0280         phy->pmd_scramble = 0 ;
0281         switch (phy->pmd_type[PMD_SK_PMD]) {
0282         case 'P' :
0283             mib->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ;
0284             break ;
0285         case 'L' :
0286             mib->fddiPORTPMDClass = MIB_PMDCLASS_LCF ;
0287             break ;
0288         case 'D' :
0289             mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
0290             break ;
0291         case 'S' :
0292             mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
0293             phy->pmd_scramble = TRUE ;
0294             break ;
0295         case 'U' :
0296             mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
0297             phy->pmd_scramble = TRUE ;
0298             break ;
0299         case '1' :
0300             mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
0301             break ;
0302         case '2' :
0303             mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
0304             break ;
0305         case '3' :
0306             mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
0307             break ;
0308         case '4' :
0309             mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
0310             break ;
0311         case 'H' :
0312             mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
0313             break ;
0314         case 'I' :
0315             mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
0316             break ;
0317         case 'G' :
0318             mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
0319             break ;
0320         default:
0321             mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
0322             break ;
0323         }
0324         /*
0325          * A and B port can be on primary and secondary path
0326          */
0327         switch (mib->fddiPORTMy_Type) {
0328         case TA :
0329             mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
0330             mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
0331             mib->fddiPORTRequestedPaths[2] =
0332                 MIB_P_PATH_LOCAL |
0333                 MIB_P_PATH_CON_ALTER |
0334                 MIB_P_PATH_SEC_PREFER ;
0335             mib->fddiPORTRequestedPaths[3] =
0336                 MIB_P_PATH_LOCAL |
0337                 MIB_P_PATH_CON_ALTER |
0338                 MIB_P_PATH_SEC_PREFER |
0339                 MIB_P_PATH_THRU ;
0340             break ;
0341         case TB :
0342             mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
0343             mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
0344             mib->fddiPORTRequestedPaths[2] =
0345                 MIB_P_PATH_LOCAL |
0346                 MIB_P_PATH_PRIM_PREFER ;
0347             mib->fddiPORTRequestedPaths[3] =
0348                 MIB_P_PATH_LOCAL |
0349                 MIB_P_PATH_PRIM_PREFER |
0350                 MIB_P_PATH_CON_PREFER |
0351                 MIB_P_PATH_THRU ;
0352             break ;
0353         case TS :
0354             mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
0355             mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
0356             mib->fddiPORTRequestedPaths[2] =
0357                 MIB_P_PATH_LOCAL |
0358                 MIB_P_PATH_CON_ALTER |
0359                 MIB_P_PATH_PRIM_PREFER ;
0360             mib->fddiPORTRequestedPaths[3] =
0361                 MIB_P_PATH_LOCAL |
0362                 MIB_P_PATH_CON_ALTER |
0363                 MIB_P_PATH_PRIM_PREFER ;
0364             break ;
0365         case TM :
0366             mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
0367             mib->fddiPORTRequestedPaths[2] =
0368                 MIB_P_PATH_LOCAL |
0369                 MIB_P_PATH_SEC_ALTER |
0370                 MIB_P_PATH_PRIM_ALTER ;
0371             mib->fddiPORTRequestedPaths[3] = 0 ;
0372             break ;
0373         }
0374 
0375         phy->pc_lem_fail = FALSE ;
0376         mib->fddiPORTPCMStateX = mib->fddiPORTPCMState ;
0377         mib->fddiPORTLCTFail_Ct = 0 ;
0378         mib->fddiPORTBS_Flag = 0 ;
0379         mib->fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
0380         mib->fddiPORTNeighborType = TNONE ;
0381         phy->ls_flag = 0 ;
0382         phy->rc_flag = 0 ;
0383         phy->tc_flag = 0 ;
0384         phy->td_flag = 0 ;
0385         if (np >= PM)
0386             phy->phy_name = '0' + np - PM ;
0387         else
0388             phy->phy_name = 'A' + np ;
0389         phy->wc_flag = FALSE ;      /* set by SMT */
0390         memset((char *)&phy->lem,0,sizeof(struct lem_counter)) ;
0391         reset_lem_struct(phy) ;
0392         memset((char *)&phy->plc,0,sizeof(struct s_plc)) ;
0393         phy->plc.p_state = PS_OFF ;
0394         for (i = 0 ; i < NUMBITS ; i++) {
0395             phy->t_next[i] = 0 ;
0396         }
0397     }
0398     real_init_plc(smc) ;
0399 }
0400 
0401 void init_plc(struct s_smc *smc)
0402 {
0403     SK_UNUSED(smc) ;
0404 
0405     /*
0406      * dummy
0407      * this is an obsolete public entry point that has to remain
0408      * for compat. It is used by various drivers.
0409      * the work is now done in real_init_plc()
0410      * which is called from pcm_init() ;
0411      */
0412 }
0413 
0414 static void real_init_plc(struct s_smc *smc)
0415 {
0416     int p ;
0417 
0418     for (p = 0 ; p < NUMPHYS ; p++)
0419         plc_init(smc,p) ;
0420 }
0421 
0422 static void plc_init(struct s_smc *smc, int p)
0423 {
0424     int i ;
0425 #ifndef MOT_ELM
0426     int rev ;   /* Revision of PLC-x */
0427 #endif  /* MOT_ELM */
0428 
0429     /* transit PCM state machine to MAINT state */
0430     outpw(PLC(p,PL_CNTRL_B),0) ;
0431     outpw(PLC(p,PL_CNTRL_B),PL_PCM_STOP) ;
0432     outpw(PLC(p,PL_CNTRL_A),0) ;
0433 
0434     /*
0435      * if PLC-S then set control register C
0436      */
0437 #ifndef MOT_ELM
0438     rev = inpw(PLC(p,PL_STATUS_A)) & PLC_REV_MASK ;
0439     if (rev != PLC_REVISION_A)
0440 #endif  /* MOT_ELM */
0441     {
0442         if (smc->y[p].pmd_scramble) {
0443             outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_S) ;
0444 #ifdef  MOT_ELM
0445             outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_S) ;
0446             outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_S) ;
0447 #endif  /* MOT_ELM */
0448         }
0449         else {
0450             outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_U) ;
0451 #ifdef  MOT_ELM
0452             outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_U) ;
0453             outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_U) ;
0454 #endif  /* MOT_ELM */
0455         }
0456     }
0457 
0458     /*
0459      * set timer register
0460      */
0461     for ( i = 0 ; pltm[i].timer; i++)   /* set timer parameter reg */
0462         outpw(PLC(p,pltm[i].timer),pltm[i].para) ;
0463 
0464     (void)inpw(PLC(p,PL_INTR_EVENT)) ;  /* clear interrupt event reg */
0465     plc_clear_irq(smc,p) ;
0466     outpw(PLC(p,PL_INTR_MASK),plc_imsk_na); /* enable non active irq's */
0467 
0468     /*
0469      * if PCM is configured for class s, it will NOT go to the
0470      * REMOVE state if offline (page 3-36;)
0471      * in the concentrator, all inactive PHYS always must be in
0472      * the remove state
0473      * there's no real need to use this feature at all ..
0474      */
0475 #ifndef CONCENTRATOR
0476     if ((smc->s.sas == SMT_SAS) && (p == PS)) {
0477         outpw(PLC(p,PL_CNTRL_B),PL_CLASS_S) ;
0478     }
0479 #endif
0480 }
0481 
0482 /*
0483  * control PCM state machine
0484  */
0485 static void plc_go_state(struct s_smc *smc, int p, int state)
0486 {
0487     HW_PTR port ;
0488     int val ;
0489 
0490     SK_UNUSED(smc) ;
0491 
0492     port = (HW_PTR) (PLC(p,PL_CNTRL_B)) ;
0493     val = inpw(port) & ~(PL_PCM_CNTRL | PL_MAINT) ;
0494     outpw(port,val) ;
0495     outpw(port,val | state) ;
0496 }
0497 
0498 /*
0499  * read current line state (called by ECM & PCM)
0500  */
0501 int sm_pm_get_ls(struct s_smc *smc, int phy)
0502 {
0503     int state ;
0504 
0505 #ifdef  CONCENTRATOR
0506     if (!plc_is_installed(smc,phy))
0507         return PC_QLS;
0508 #endif
0509 
0510     state = inpw(PLC(phy,PL_STATUS_A)) & PL_LINE_ST ;
0511     switch(state) {
0512     case PL_L_QLS:
0513         state = PC_QLS ;
0514         break ;
0515     case PL_L_MLS:
0516         state = PC_MLS ;
0517         break ;
0518     case PL_L_HLS:
0519         state = PC_HLS ;
0520         break ;
0521     case PL_L_ILS4:
0522     case PL_L_ILS16:
0523         state = PC_ILS ;
0524         break ;
0525     case PL_L_ALS:
0526         state = PC_LS_PDR ;
0527         break ;
0528     default :
0529         state = PC_LS_NONE ;
0530     }
0531     return state;
0532 }
0533 
0534 static int plc_send_bits(struct s_smc *smc, struct s_phy *phy, int len)
0535 {
0536     int np = phy->np ;      /* PHY index */
0537     int n ;
0538     int i ;
0539 
0540     SK_UNUSED(smc) ;
0541 
0542     /* create bit vector */
0543     for (i = len-1,n = 0 ; i >= 0 ; i--) {
0544         n = (n<<1) | phy->t_val[phy->bitn+i] ;
0545     }
0546     if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
0547 #if 0
0548         printf("PL_PCM_SIGNAL is set\n") ;
0549 #endif
0550         return 1;
0551     }
0552     /* write bit[n] & length = 1 to regs */
0553     outpw(PLC(np,PL_VECTOR_LEN),len-1) ;    /* len=nr-1 */
0554     outpw(PLC(np,PL_XMIT_VECTOR),n) ;
0555 #ifdef  DEBUG
0556 #if 1
0557 #ifdef  DEBUG_BRD
0558     if (smc->debug.d_plc & 0x80)
0559 #else
0560     if (debug.d_plc & 0x80)
0561 #endif
0562         printf("SIGNALING bit %d .. %d\n",phy->bitn,phy->bitn+len-1) ;
0563 #endif
0564 #endif
0565     return 0;
0566 }
0567 
0568 /*
0569  * config plc muxes
0570  */
0571 void plc_config_mux(struct s_smc *smc, int mux)
0572 {
0573     if (smc->s.sas != SMT_DAS)
0574         return ;
0575     if (mux == MUX_WRAPB) {
0576         SETMASK(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
0577         SETMASK(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
0578     }
0579     else {
0580         CLEAR(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
0581         CLEAR(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP) ;
0582     }
0583     CLEAR(PLC(PB,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
0584     CLEAR(PLC(PB,PL_CNTRL_A),PL_SC_REM_LOOP) ;
0585 }
0586 
0587 /*
0588     PCM state machine
0589     called by dispatcher  & fddi_init() (driver)
0590     do
0591         display state change
0592         process event
0593     until SM is stable
0594 */
0595 void pcm(struct s_smc *smc, const int np, int event)
0596 {
0597     int state ;
0598     int oldstate ;
0599     struct s_phy    *phy ;
0600     struct fddi_mib_p   *mib ;
0601 
0602 #ifndef CONCENTRATOR
0603     /*
0604      * ignore 2nd PHY if SAS
0605      */
0606     if ((np != PS) && (smc->s.sas == SMT_SAS))
0607         return ;
0608 #endif
0609     phy = &smc->y[np] ;
0610     mib = phy->mib ;
0611     oldstate = mib->fddiPORTPCMState ;
0612     do {
0613         DB_PCM("PCM %c: state %s",
0614             phy->phy_name,
0615             (mib->fddiPORTPCMState & AFLAG) ? "ACTIONS " : "") ;
0616         DB_PCM("%s, event %s\n",
0617             pcm_states[mib->fddiPORTPCMState & ~AFLAG],
0618             pcm_events[event]) ;
0619         state = mib->fddiPORTPCMState ;
0620         pcm_fsm(smc,phy,event) ;
0621         event = 0 ;
0622     } while (state != mib->fddiPORTPCMState) ;
0623     /*
0624      * because the PLC does the bit signaling for us,
0625      * we're always in SIGNAL state
0626      * the MIB want's to see CONNECT
0627      * we therefore fake an entry in the MIB
0628      */
0629     if (state == PC5_SIGNAL)
0630         mib->fddiPORTPCMStateX = PC3_CONNECT ;
0631     else
0632         mib->fddiPORTPCMStateX = state ;
0633 
0634 #ifndef SLIM_SMT
0635     /*
0636      * path change
0637      */
0638     if (    mib->fddiPORTPCMState != oldstate &&
0639         ((oldstate == PC8_ACTIVE) || (mib->fddiPORTPCMState == PC8_ACTIVE))) {
0640         smt_srf_event(smc,SMT_EVENT_PORT_PATH_CHANGE,
0641             (int) (INDEX_PORT+ phy->np),0) ;
0642     }
0643 #endif
0644 
0645 #ifdef FDDI_MIB
0646     /* check whether a snmp-trap has to be sent */
0647 
0648     if ( mib->fddiPORTPCMState != oldstate ) {
0649         /* a real state change took place */
0650         DB_SNMP ("PCM from %d to %d\n", oldstate, mib->fddiPORTPCMState);
0651         if ( mib->fddiPORTPCMState == PC0_OFF ) {
0652             /* send first trap */
0653             snmp_fddi_trap (smc, 1, (int) mib->fddiPORTIndex );
0654         } else if ( oldstate == PC0_OFF ) {
0655             /* send second trap */
0656             snmp_fddi_trap (smc, 2, (int) mib->fddiPORTIndex );
0657         } else if ( mib->fddiPORTPCMState != PC2_TRACE &&
0658             oldstate == PC8_ACTIVE ) {
0659             /* send third trap */
0660             snmp_fddi_trap (smc, 3, (int) mib->fddiPORTIndex );
0661         } else if ( mib->fddiPORTPCMState == PC8_ACTIVE ) {
0662             /* send fourth trap */
0663             snmp_fddi_trap (smc, 4, (int) mib->fddiPORTIndex );
0664         }
0665     }
0666 #endif
0667 
0668     pcm_state_change(smc,np,state) ;
0669 }
0670 
0671 /*
0672  * PCM state machine
0673  */
0674 static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd)
0675 {
0676     int i ;
0677     int np = phy->np ;      /* PHY index */
0678     struct s_plc    *plc ;
0679     struct fddi_mib_p   *mib ;
0680 #ifndef MOT_ELM
0681     u_short plc_rev ;       /* Revision of the plc */
0682 #endif  /* nMOT_ELM */
0683 
0684     plc = &phy->plc ;
0685     mib = phy->mib ;
0686 
0687     /*
0688      * general transitions independent of state
0689      */
0690     switch (cmd) {
0691     case PC_STOP :
0692         /*PC00-PC80*/
0693         if (mib->fddiPORTPCMState != PC9_MAINT) {
0694             GO_STATE(PC0_OFF) ;
0695             AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
0696                 FDDI_PORT_EVENT, (u_long) FDDI_PORT_STOP,
0697                 smt_get_port_event_word(smc));
0698         }
0699         return ;
0700     case PC_START :
0701         /*PC01-PC81*/
0702         if (mib->fddiPORTPCMState != PC9_MAINT)
0703             GO_STATE(PC1_BREAK) ;
0704         return ;
0705     case PC_DISABLE :
0706         /* PC09-PC99 */
0707         GO_STATE(PC9_MAINT) ;
0708         AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
0709             FDDI_PORT_EVENT, (u_long) FDDI_PORT_DISABLED,
0710             smt_get_port_event_word(smc));
0711         return ;
0712     case PC_TIMEOUT_LCT :
0713         /* if long or extended LCT */
0714         stop_pcm_timer0(smc,phy) ;
0715         CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
0716         /* end of LCT is indicate by PCM_CODE (initiate PCM event) */
0717         return ;
0718     }
0719 
0720     switch(mib->fddiPORTPCMState) {
0721     case ACTIONS(PC0_OFF) :
0722         stop_pcm_timer0(smc,phy) ;
0723         outpw(PLC(np,PL_CNTRL_A),0) ;
0724         CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
0725         CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
0726         sm_ph_lem_stop(smc,np) ;        /* disable LEM */
0727         phy->cf_loop = FALSE ;
0728         phy->cf_join = FALSE ;
0729         queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
0730         plc_go_state(smc,np,PL_PCM_STOP) ;
0731         mib->fddiPORTConnectState = PCM_DISABLED ;
0732         ACTIONS_DONE() ;
0733         break ;
0734     case PC0_OFF:
0735         /*PC09*/
0736         if (cmd == PC_MAINT) {
0737             GO_STATE(PC9_MAINT) ;
0738             break ;
0739         }
0740         break ;
0741     case ACTIONS(PC1_BREAK) :
0742         /* Stop the LCT timer if we came from Signal state */
0743         stop_pcm_timer0(smc,phy) ;
0744         ACTIONS_DONE() ;
0745         plc_go_state(smc,np,0) ;
0746         CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
0747         CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
0748         sm_ph_lem_stop(smc,np) ;        /* disable LEM */
0749         /*
0750          * if vector is already loaded, go to OFF to clear PCM_SIGNAL
0751          */
0752 #if 0
0753         if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
0754             plc_go_state(smc,np,PL_PCM_STOP) ;
0755             /* TB_MIN ? */
0756         }
0757 #endif
0758         /*
0759          * Go to OFF state in any case.
0760          */
0761         plc_go_state(smc,np,PL_PCM_STOP) ;
0762 
0763         if (mib->fddiPORTPC_Withhold == PC_WH_NONE)
0764             mib->fddiPORTConnectState = PCM_CONNECTING ;
0765         phy->cf_loop = FALSE ;
0766         phy->cf_join = FALSE ;
0767         queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
0768         phy->ls_flag = FALSE ;
0769         phy->pc_mode = PM_NONE ;    /* needed by CFM */
0770         phy->bitn = 0 ;         /* bit signaling start bit */
0771         for (i = 0 ; i < 3 ; i++)
0772             pc_tcode_actions(smc,i,phy) ;
0773 
0774         /* Set the non-active interrupt mask register */
0775         outpw(PLC(np,PL_INTR_MASK),plc_imsk_na) ;
0776 
0777         /*
0778          * If the LCT was stopped. There might be a
0779          * PCM_CODE interrupt event present.
0780          * This must be cleared.
0781          */
0782         (void)inpw(PLC(np,PL_INTR_EVENT)) ;
0783 #ifndef MOT_ELM
0784         /* Get the plc revision for revision dependent code */
0785         plc_rev = inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK ;
0786 
0787         if (plc_rev != PLC_REV_SN3)
0788 #endif  /* MOT_ELM */
0789         {
0790             /*
0791              * No supernet III PLC, so set Xmit verctor and
0792              * length BEFORE starting the state machine.
0793              */
0794             if (plc_send_bits(smc,phy,3)) {
0795                 return ;
0796             }
0797         }
0798 
0799         /*
0800          * Now give the Start command.
0801          * - The start command shall be done before setting the bits
0802          *   to be signaled. (In PLC-S description and PLCS in SN3.
0803          * - The start command shall be issued AFTER setting the
0804          *   XMIT vector and the XMIT length register.
0805          *
0806          * We do it exactly according this specs for the old PLC and
0807          * the new PLCS inside the SN3.
0808          * For the usual PLCS we try it the way it is done for the
0809          * old PLC and set the XMIT registers again, if the PLC is
0810          * not in SIGNAL state. This is done according to an PLCS
0811          * errata workaround.
0812          */
0813 
0814         plc_go_state(smc,np,PL_PCM_START) ;
0815 
0816         /*
0817          * workaround for PLC-S eng. sample errata
0818          */
0819 #ifdef  MOT_ELM
0820         if (!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
0821 #else   /* nMOT_ELM */
0822         if (((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) !=
0823             PLC_REVISION_A) &&
0824             !(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
0825 #endif  /* nMOT_ELM */
0826         {
0827             /*
0828              * Set register again (PLCS errata) or the first time
0829              * (new SN3 PLCS).
0830              */
0831             (void) plc_send_bits(smc,phy,3) ;
0832         }
0833         /*
0834          * end of workaround
0835          */
0836 
0837         GO_STATE(PC5_SIGNAL) ;
0838         plc->p_state = PS_BIT3 ;
0839         plc->p_bits = 3 ;
0840         plc->p_start = 0 ;
0841 
0842         break ;
0843     case PC1_BREAK :
0844         break ;
0845     case ACTIONS(PC2_TRACE) :
0846         plc_go_state(smc,np,PL_PCM_TRACE) ;
0847         ACTIONS_DONE() ;
0848         break ;
0849     case PC2_TRACE :
0850         break ;
0851 
0852     case PC3_CONNECT :  /* these states are done by hardware */
0853     case PC4_NEXT :
0854         break ;
0855 
0856     case ACTIONS(PC5_SIGNAL) :
0857         ACTIONS_DONE() ;
0858     case PC5_SIGNAL :
0859         if ((cmd != PC_SIGNAL) && (cmd != PC_TIMEOUT_LCT))
0860             break ;
0861         switch (plc->p_state) {
0862         case PS_BIT3 :
0863             for (i = 0 ; i <= 2 ; i++)
0864                 pc_rcode_actions(smc,i,phy) ;
0865             pc_tcode_actions(smc,3,phy) ;
0866             plc->p_state = PS_BIT4 ;
0867             plc->p_bits = 1 ;
0868             plc->p_start = 3 ;
0869             phy->bitn = 3 ;
0870             if (plc_send_bits(smc,phy,1)) {
0871                 return ;
0872             }
0873             break ;
0874         case PS_BIT4 :
0875             pc_rcode_actions(smc,3,phy) ;
0876             for (i = 4 ; i <= 6 ; i++)
0877                 pc_tcode_actions(smc,i,phy) ;
0878             plc->p_state = PS_BIT7 ;
0879             plc->p_bits = 3 ;
0880             plc->p_start = 4 ;
0881             phy->bitn = 4 ;
0882             if (plc_send_bits(smc,phy,3)) {
0883                 return ;
0884             }
0885             break ;
0886         case PS_BIT7 :
0887             for (i = 3 ; i <= 6 ; i++)
0888                 pc_rcode_actions(smc,i,phy) ;
0889             plc->p_state = PS_LCT ;
0890             plc->p_bits = 0 ;
0891             plc->p_start = 7 ;
0892             phy->bitn = 7 ;
0893         sm_ph_lem_start(smc,np,(int)smc->s.lct_short) ; /* enable LEM */
0894             /* start LCT */
0895             i = inpw(PLC(np,PL_CNTRL_B)) & ~PL_PC_LOOP ;
0896             outpw(PLC(np,PL_CNTRL_B),i) ;   /* must be cleared */
0897             outpw(PLC(np,PL_CNTRL_B),i | PL_RLBP) ;
0898             break ;
0899         case PS_LCT :
0900             /* check for local LCT failure */
0901             pc_tcode_actions(smc,7,phy) ;
0902             /*
0903              * set tval[7]
0904              */
0905             plc->p_state = PS_BIT8 ;
0906             plc->p_bits = 1 ;
0907             plc->p_start = 7 ;
0908             phy->bitn = 7 ;
0909             if (plc_send_bits(smc,phy,1)) {
0910                 return ;
0911             }
0912             break ;
0913         case PS_BIT8 :
0914             /* check for remote LCT failure */
0915             pc_rcode_actions(smc,7,phy) ;
0916             if (phy->t_val[7] || phy->r_val[7]) {
0917                 plc_go_state(smc,np,PL_PCM_STOP) ;
0918                 GO_STATE(PC1_BREAK) ;
0919                 break ;
0920             }
0921             for (i = 8 ; i <= 9 ; i++)
0922                 pc_tcode_actions(smc,i,phy) ;
0923             plc->p_state = PS_JOIN ;
0924             plc->p_bits = 2 ;
0925             plc->p_start = 8 ;
0926             phy->bitn = 8 ;
0927             if (plc_send_bits(smc,phy,2)) {
0928                 return ;
0929             }
0930             break ;
0931         case PS_JOIN :
0932             for (i = 8 ; i <= 9 ; i++)
0933                 pc_rcode_actions(smc,i,phy) ;
0934             plc->p_state = PS_ACTIVE ;
0935             GO_STATE(PC6_JOIN) ;
0936             break ;
0937         }
0938         break ;
0939 
0940     case ACTIONS(PC6_JOIN) :
0941         /*
0942          * prevent mux error when going from WRAP_A to WRAP_B
0943          */
0944         if (smc->s.sas == SMT_DAS && np == PB &&
0945             (smc->y[PA].pc_mode == PM_TREE ||
0946              smc->y[PB].pc_mode == PM_TREE)) {
0947             SETMASK(PLC(np,PL_CNTRL_A),
0948                 PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
0949             SETMASK(PLC(np,PL_CNTRL_B),
0950                 PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
0951         }
0952         SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
0953         SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
0954         ACTIONS_DONE() ;
0955         cmd = 0 ;
0956         /* fall thru */
0957     case PC6_JOIN :
0958         switch (plc->p_state) {
0959         case PS_ACTIVE:
0960             /*PC88b*/
0961             if (!phy->cf_join) {
0962                 phy->cf_join = TRUE ;
0963                 queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
0964             }
0965             if (cmd == PC_JOIN)
0966                 GO_STATE(PC8_ACTIVE) ;
0967             /*PC82*/
0968             if (cmd == PC_TRACE) {
0969                 GO_STATE(PC2_TRACE) ;
0970                 break ;
0971             }
0972             break ;
0973         }
0974         break ;
0975 
0976     case PC7_VERIFY :
0977         break ;
0978 
0979     case ACTIONS(PC8_ACTIVE) :
0980         /*
0981          * start LEM for SMT
0982          */
0983         sm_ph_lem_start(smc,(int)phy->np,LCT_LEM_MAX) ;
0984 
0985         phy->tr_flag = FALSE ;
0986         mib->fddiPORTConnectState = PCM_ACTIVE ;
0987 
0988         /* Set the active interrupt mask register */
0989         outpw(PLC(np,PL_INTR_MASK),plc_imsk_act) ;
0990 
0991         ACTIONS_DONE() ;
0992         break ;
0993     case PC8_ACTIVE :
0994         /*PC81 is done by PL_TNE_EXPIRED irq */
0995         /*PC82*/
0996         if (cmd == PC_TRACE) {
0997             GO_STATE(PC2_TRACE) ;
0998             break ;
0999         }
1000         /*PC88c: is done by TRACE_PROP irq */
1001 
1002         break ;
1003     case ACTIONS(PC9_MAINT) :
1004         stop_pcm_timer0(smc,phy) ;
1005         CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
1006         CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
1007         CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ; /* disable LEM int. */
1008         sm_ph_lem_stop(smc,np) ;        /* disable LEM */
1009         phy->cf_loop = FALSE ;
1010         phy->cf_join = FALSE ;
1011         queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
1012         plc_go_state(smc,np,PL_PCM_STOP) ;
1013         mib->fddiPORTConnectState = PCM_DISABLED ;
1014         SETMASK(PLC(np,PL_CNTRL_B),PL_MAINT,PL_MAINT) ;
1015         sm_ph_linestate(smc,np,(int) MIB2LS(mib->fddiPORTMaint_LS)) ;
1016         outpw(PLC(np,PL_CNTRL_A),PL_SC_BYPASS) ;
1017         ACTIONS_DONE() ;
1018         break ;
1019     case PC9_MAINT :
1020         DB_PCMN(1,"PCM %c : MAINT\n",phy->phy_name,0) ;
1021         /*PC90*/
1022         if (cmd == PC_ENABLE) {
1023             GO_STATE(PC0_OFF) ;
1024             break ;
1025         }
1026         break ;
1027 
1028     default:
1029         SMT_PANIC(smc,SMT_E0118, SMT_E0118_MSG) ;
1030         break ;
1031     }
1032 }
1033 
1034 /*
1035  * force line state on a PHY output (only in MAINT state)
1036  */
1037 static void sm_ph_linestate(struct s_smc *smc, int phy, int ls)
1038 {
1039     int cntrl ;
1040 
1041     SK_UNUSED(smc) ;
1042 
1043     cntrl = (inpw(PLC(phy,PL_CNTRL_B)) & ~PL_MAINT_LS) |
1044                         PL_PCM_STOP | PL_MAINT ;
1045     switch(ls) {
1046     case PC_QLS:        /* Force Quiet */
1047         cntrl |= PL_M_QUI0 ;
1048         break ;
1049     case PC_MLS:        /* Force Master */
1050         cntrl |= PL_M_MASTR ;
1051         break ;
1052     case PC_HLS:        /* Force Halt */
1053         cntrl |= PL_M_HALT ;
1054         break ;
1055     default :
1056     case PC_ILS:        /* Force Idle */
1057         cntrl |= PL_M_IDLE ;
1058         break ;
1059     case PC_LS_PDR:     /* Enable repeat filter */
1060         cntrl |= PL_M_TPDR ;
1061         break ;
1062     }
1063     outpw(PLC(phy,PL_CNTRL_B),cntrl) ;
1064 }
1065 
1066 static void reset_lem_struct(struct s_phy *phy)
1067 {
1068     struct lem_counter *lem = &phy->lem ;
1069 
1070     phy->mib->fddiPORTLer_Estimate = 15 ;
1071     lem->lem_float_ber = 15 * 100 ;
1072 }
1073 
1074 /*
1075  * link error monitor
1076  */
1077 static void lem_evaluate(struct s_smc *smc, struct s_phy *phy)
1078 {
1079     int ber ;
1080     u_long errors ;
1081     struct lem_counter *lem = &phy->lem ;
1082     struct fddi_mib_p   *mib ;
1083     int         cond ;
1084 
1085     mib = phy->mib ;
1086 
1087     if (!lem->lem_on)
1088         return ;
1089 
1090     errors = inpw(PLC(((int) phy->np),PL_LINK_ERR_CTR)) ;
1091     lem->lem_errors += errors ;
1092     mib->fddiPORTLem_Ct += errors ;
1093 
1094     errors = lem->lem_errors ;
1095     /*
1096      * calculation is called on a intervall of 8 seconds
1097      *  -> this means, that one error in 8 sec. is one of 8*125*10E6
1098      *  the same as BER = 10E-9
1099      * Please note:
1100      *  -> 9 errors in 8 seconds mean:
1101      *     BER = 9 * 10E-9  and this is
1102      *      < 10E-8, so the limit of 10E-8 is not reached!
1103      */
1104 
1105         if (!errors)        ber = 15 ;
1106     else    if (errors <= 9)    ber = 9 ;
1107     else    if (errors <= 99)   ber = 8 ;
1108     else    if (errors <= 999)  ber = 7 ;
1109     else    if (errors <= 9999) ber = 6 ;
1110     else    if (errors <= 99999)    ber = 5 ;
1111     else    if (errors <= 999999)   ber = 4 ;
1112     else    if (errors <= 9999999)  ber = 3 ;
1113     else    if (errors <= 99999999) ber = 2 ;
1114     else    if (errors <= 999999999) ber = 1 ;
1115     else                ber = 0 ;
1116 
1117     /*
1118      * weighted average
1119      */
1120     ber *= 100 ;
1121     lem->lem_float_ber = lem->lem_float_ber * 7 + ber * 3 ;
1122     lem->lem_float_ber /= 10 ;
1123     mib->fddiPORTLer_Estimate = lem->lem_float_ber / 100 ;
1124     if (mib->fddiPORTLer_Estimate < 4) {
1125         mib->fddiPORTLer_Estimate = 4 ;
1126     }
1127 
1128     if (lem->lem_errors) {
1129         DB_PCMN(1,"LEM %c :\n",phy->np == PB? 'B' : 'A',0) ;
1130         DB_PCMN(1,"errors      : %ld\n",lem->lem_errors,0) ;
1131         DB_PCMN(1,"sum_errors  : %ld\n",mib->fddiPORTLem_Ct,0) ;
1132         DB_PCMN(1,"current BER : 10E-%d\n",ber/100,0) ;
1133         DB_PCMN(1,"float BER   : 10E-(%d/100)\n",lem->lem_float_ber,0) ;
1134         DB_PCMN(1,"avg. BER    : 10E-%d\n",
1135             mib->fddiPORTLer_Estimate,0) ;
1136     }
1137 
1138     lem->lem_errors = 0L ;
1139 
1140 #ifndef SLIM_SMT
1141     cond = (mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Alarm) ?
1142         TRUE : FALSE ;
1143 #ifdef  SMT_EXT_CUTOFF
1144     smt_ler_alarm_check(smc,phy,cond) ;
1145 #endif  /* nSMT_EXT_CUTOFF */
1146     if (cond != mib->fddiPORTLerFlag) {
1147         smt_srf_event(smc,SMT_COND_PORT_LER,
1148             (int) (INDEX_PORT+ phy->np) ,cond) ;
1149     }
1150 #endif
1151 
1152     if (    mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Cutoff) {
1153         phy->pc_lem_fail = TRUE ;       /* flag */
1154         mib->fddiPORTLem_Reject_Ct++ ;
1155         /*
1156          * "forgive 10e-2" if we cutoff so we can come
1157          * up again ..
1158          */
1159         lem->lem_float_ber += 2*100 ;
1160 
1161         /*PC81b*/
1162 #ifdef  CONCENTRATOR
1163         DB_PCMN(1,"PCM: LER cutoff on port %d cutoff %d\n",
1164             phy->np, mib->fddiPORTLer_Cutoff) ;
1165 #endif
1166 #ifdef  SMT_EXT_CUTOFF
1167         smt_port_off_event(smc,phy->np);
1168 #else   /* nSMT_EXT_CUTOFF */
1169         queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
1170 #endif  /* nSMT_EXT_CUTOFF */
1171     }
1172 }
1173 
1174 /*
1175  * called by SMT to calculate LEM bit error rate
1176  */
1177 void sm_lem_evaluate(struct s_smc *smc)
1178 {
1179     int np ;
1180 
1181     for (np = 0 ; np < NUMPHYS ; np++)
1182         lem_evaluate(smc,&smc->y[np]) ;
1183 }
1184 
1185 static void lem_check_lct(struct s_smc *smc, struct s_phy *phy)
1186 {
1187     struct lem_counter  *lem = &phy->lem ;
1188     struct fddi_mib_p   *mib ;
1189     int errors ;
1190 
1191     mib = phy->mib ;
1192 
1193     phy->pc_lem_fail = FALSE ;      /* flag */
1194     errors = inpw(PLC(((int)phy->np),PL_LINK_ERR_CTR)) ;
1195     lem->lem_errors += errors ;
1196     mib->fddiPORTLem_Ct += errors ;
1197     if (lem->lem_errors) {
1198         switch(phy->lc_test) {
1199         case LC_SHORT:
1200             if (lem->lem_errors >= smc->s.lct_short)
1201                 phy->pc_lem_fail = TRUE ;
1202             break ;
1203         case LC_MEDIUM:
1204             if (lem->lem_errors >= smc->s.lct_medium)
1205                 phy->pc_lem_fail = TRUE ;
1206             break ;
1207         case LC_LONG:
1208             if (lem->lem_errors >= smc->s.lct_long)
1209                 phy->pc_lem_fail = TRUE ;
1210             break ;
1211         case LC_EXTENDED:
1212             if (lem->lem_errors >= smc->s.lct_extended)
1213                 phy->pc_lem_fail = TRUE ;
1214             break ;
1215         }
1216         DB_PCMN(1," >>errors : %d\n",lem->lem_errors,0) ;
1217     }
1218     if (phy->pc_lem_fail) {
1219         mib->fddiPORTLCTFail_Ct++ ;
1220         mib->fddiPORTLem_Reject_Ct++ ;
1221     }
1222     else
1223         mib->fddiPORTLCTFail_Ct = 0 ;
1224 }
1225 
1226 /*
1227  * LEM functions
1228  */
1229 static void sm_ph_lem_start(struct s_smc *smc, int np, int threshold)
1230 {
1231     struct lem_counter *lem = &smc->y[np].lem ;
1232 
1233     lem->lem_on = 1 ;
1234     lem->lem_errors = 0L ;
1235 
1236     /* Do NOT reset mib->fddiPORTLer_Estimate here. It is called too
1237      * often.
1238      */
1239 
1240     outpw(PLC(np,PL_LE_THRESHOLD),threshold) ;
1241     (void)inpw(PLC(np,PL_LINK_ERR_CTR)) ;   /* clear error counter */
1242 
1243     /* enable LE INT */
1244     SETMASK(PLC(np,PL_INTR_MASK),PL_LE_CTR,PL_LE_CTR) ;
1245 }
1246 
1247 static void sm_ph_lem_stop(struct s_smc *smc, int np)
1248 {
1249     struct lem_counter *lem = &smc->y[np].lem ;
1250 
1251     lem->lem_on = 0 ;
1252     CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ;
1253 }
1254 
1255 /* ARGSUSED */
1256 void sm_pm_ls_latch(struct s_smc *smc, int phy, int on_off)
1257 /* int on_off;  en- or disable ident. ls */
1258 {
1259     SK_UNUSED(smc) ;
1260 
1261     phy = phy ; on_off = on_off ;
1262 }
1263 
1264 
1265 /*
1266  * PCM pseudo code
1267  * receive actions are called AFTER the bit n is received,
1268  * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received
1269  */
1270 
1271 /*
1272  * PCM pseudo code 5.1 .. 6.1
1273  */
1274 static void pc_rcode_actions(struct s_smc *smc, int bit, struct s_phy *phy)
1275 {
1276     struct fddi_mib_p   *mib ;
1277 
1278     mib = phy->mib ;
1279 
1280     DB_PCMN(1,"SIG rec %x %x:\n", bit,phy->r_val[bit] ) ;
1281     bit++ ;
1282 
1283     switch(bit) {
1284     case 0:
1285     case 1:
1286     case 2:
1287         break ;
1288     case 3 :
1289         if (phy->r_val[1] == 0 && phy->r_val[2] == 0)
1290             mib->fddiPORTNeighborType = TA ;
1291         else if (phy->r_val[1] == 0 && phy->r_val[2] == 1)
1292             mib->fddiPORTNeighborType = TB ;
1293         else if (phy->r_val[1] == 1 && phy->r_val[2] == 0)
1294             mib->fddiPORTNeighborType = TS ;
1295         else if (phy->r_val[1] == 1 && phy->r_val[2] == 1)
1296             mib->fddiPORTNeighborType = TM ;
1297         break ;
1298     case 4:
1299         if (mib->fddiPORTMy_Type == TM &&
1300             mib->fddiPORTNeighborType == TM) {
1301             DB_PCMN(1,"PCM %c : E100 withhold M-M\n",
1302                 phy->phy_name,0) ;
1303             mib->fddiPORTPC_Withhold = PC_WH_M_M ;
1304             RS_SET(smc,RS_EVENT) ;
1305         }
1306         else if (phy->t_val[3] || phy->r_val[3]) {
1307             mib->fddiPORTPC_Withhold = PC_WH_NONE ;
1308             if (mib->fddiPORTMy_Type == TM ||
1309                 mib->fddiPORTNeighborType == TM)
1310                 phy->pc_mode = PM_TREE ;
1311             else
1312                 phy->pc_mode = PM_PEER ;
1313 
1314             /* reevaluate the selection criteria (wc_flag) */
1315             all_selection_criteria (smc);
1316 
1317             if (phy->wc_flag) {
1318                 mib->fddiPORTPC_Withhold = PC_WH_PATH ;
1319             }
1320         }
1321         else {
1322             mib->fddiPORTPC_Withhold = PC_WH_OTHER ;
1323             RS_SET(smc,RS_EVENT) ;
1324             DB_PCMN(1,"PCM %c : E101 withhold other\n",
1325                 phy->phy_name,0) ;
1326         }
1327         phy->twisted = ((mib->fddiPORTMy_Type != TS) &&
1328                 (mib->fddiPORTMy_Type != TM) &&
1329                 (mib->fddiPORTNeighborType ==
1330                 mib->fddiPORTMy_Type)) ;
1331         if (phy->twisted) {
1332             DB_PCMN(1,"PCM %c : E102 !!! TWISTED !!!\n",
1333                 phy->phy_name,0) ;
1334         }
1335         break ;
1336     case 5 :
1337         break ;
1338     case 6:
1339         if (phy->t_val[4] || phy->r_val[4]) {
1340             if ((phy->t_val[4] && phy->t_val[5]) ||
1341                 (phy->r_val[4] && phy->r_val[5]) )
1342                 phy->lc_test = LC_EXTENDED ;
1343             else
1344                 phy->lc_test = LC_LONG ;
1345         }
1346         else if (phy->t_val[5] || phy->r_val[5])
1347             phy->lc_test = LC_MEDIUM ;
1348         else
1349             phy->lc_test = LC_SHORT ;
1350         switch (phy->lc_test) {
1351         case LC_SHORT :             /* 50ms */
1352             outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LENGTH ) ;
1353             phy->t_next[7] = smc->s.pcm_lc_short ;
1354             break ;
1355         case LC_MEDIUM :            /* 500ms */
1356             outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LONGLN ) ;
1357             phy->t_next[7] = smc->s.pcm_lc_medium ;
1358             break ;
1359         case LC_LONG :
1360             SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
1361             phy->t_next[7] = smc->s.pcm_lc_long ;
1362             break ;
1363         case LC_EXTENDED :
1364             SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
1365             phy->t_next[7] = smc->s.pcm_lc_extended ;
1366             break ;
1367         }
1368         if (phy->t_next[7] > smc->s.pcm_lc_medium) {
1369             start_pcm_timer0(smc,phy->t_next[7],PC_TIMEOUT_LCT,phy);
1370         }
1371         DB_PCMN(1,"LCT timer = %ld us\n", phy->t_next[7], 0) ;
1372         phy->t_next[9] = smc->s.pcm_t_next_9 ;
1373         break ;
1374     case 7:
1375         if (phy->t_val[6]) {
1376             phy->cf_loop = TRUE ;
1377         }
1378         phy->td_flag = TRUE ;
1379         break ;
1380     case 8:
1381         if (phy->t_val[7] || phy->r_val[7]) {
1382             DB_PCMN(1,"PCM %c : E103 LCT fail %s\n",
1383                 phy->phy_name,phy->t_val[7]? "local":"remote") ;
1384             queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
1385         }
1386         break ;
1387     case 9:
1388         if (phy->t_val[8] || phy->r_val[8]) {
1389             if (phy->t_val[8])
1390                 phy->cf_loop = TRUE ;
1391             phy->td_flag = TRUE ;
1392         }
1393         break ;
1394     case 10:
1395         if (phy->r_val[9]) {
1396             /* neighbor intends to have MAC on output */ ;
1397             mib->fddiPORTMacIndicated.R_val = TRUE ;
1398         }
1399         else {
1400             /* neighbor does not intend to have MAC on output */ ;
1401             mib->fddiPORTMacIndicated.R_val = FALSE ;
1402         }
1403         break ;
1404     }
1405 }
1406 
1407 /*
1408  * PCM pseudo code 5.1 .. 6.1
1409  */
1410 static void pc_tcode_actions(struct s_smc *smc, const int bit, struct s_phy *phy)
1411 {
1412     int np = phy->np ;
1413     struct fddi_mib_p   *mib ;
1414 
1415     mib = phy->mib ;
1416 
1417     switch(bit) {
1418     case 0:
1419         phy->t_val[0] = 0 ;     /* no escape used */
1420         break ;
1421     case 1:
1422         if (mib->fddiPORTMy_Type == TS || mib->fddiPORTMy_Type == TM)
1423             phy->t_val[1] = 1 ;
1424         else
1425             phy->t_val[1] = 0 ;
1426         break ;
1427     case 2 :
1428         if (mib->fddiPORTMy_Type == TB || mib->fddiPORTMy_Type == TM)
1429             phy->t_val[2] = 1 ;
1430         else
1431             phy->t_val[2] = 0 ;
1432         break ;
1433     case 3:
1434         {
1435         int type,ne ;
1436         int policy ;
1437 
1438         type = mib->fddiPORTMy_Type ;
1439         ne = mib->fddiPORTNeighborType ;
1440         policy = smc->mib.fddiSMTConnectionPolicy ;
1441 
1442         phy->t_val[3] = 1 ; /* Accept connection */
1443         switch (type) {
1444         case TA :
1445             if (
1446                 ((policy & POLICY_AA) && ne == TA) ||
1447                 ((policy & POLICY_AB) && ne == TB) ||
1448                 ((policy & POLICY_AS) && ne == TS) ||
1449                 ((policy & POLICY_AM) && ne == TM) )
1450                 phy->t_val[3] = 0 ; /* Reject */
1451             break ;
1452         case TB :
1453             if (
1454                 ((policy & POLICY_BA) && ne == TA) ||
1455                 ((policy & POLICY_BB) && ne == TB) ||
1456                 ((policy & POLICY_BS) && ne == TS) ||
1457                 ((policy & POLICY_BM) && ne == TM) )
1458                 phy->t_val[3] = 0 ; /* Reject */
1459             break ;
1460         case TS :
1461             if (
1462                 ((policy & POLICY_SA) && ne == TA) ||
1463                 ((policy & POLICY_SB) && ne == TB) ||
1464                 ((policy & POLICY_SS) && ne == TS) ||
1465                 ((policy & POLICY_SM) && ne == TM) )
1466                 phy->t_val[3] = 0 ; /* Reject */
1467             break ;
1468         case TM :
1469             if (    ne == TM ||
1470                 ((policy & POLICY_MA) && ne == TA) ||
1471                 ((policy & POLICY_MB) && ne == TB) ||
1472                 ((policy & POLICY_MS) && ne == TS) ||
1473                 ((policy & POLICY_MM) && ne == TM) )
1474                 phy->t_val[3] = 0 ; /* Reject */
1475             break ;
1476         }
1477 #ifndef SLIM_SMT
1478         /*
1479          * detect undesirable connection attempt event
1480          */
1481         if (    (type == TA && ne == TA ) ||
1482             (type == TA && ne == TS ) ||
1483             (type == TB && ne == TB ) ||
1484             (type == TB && ne == TS ) ||
1485             (type == TS && ne == TA ) ||
1486             (type == TS && ne == TB ) ) {
1487             smt_srf_event(smc,SMT_EVENT_PORT_CONNECTION,
1488                 (int) (INDEX_PORT+ phy->np) ,0) ;
1489         }
1490 #endif
1491         }
1492         break ;
1493     case 4:
1494         if (mib->fddiPORTPC_Withhold == PC_WH_NONE) {
1495             if (phy->pc_lem_fail) {
1496                 phy->t_val[4] = 1 ; /* long */
1497                 phy->t_val[5] = 0 ;
1498             }
1499             else {
1500                 phy->t_val[4] = 0 ;
1501                 if (mib->fddiPORTLCTFail_Ct > 0)
1502                     phy->t_val[5] = 1 ; /* medium */
1503                 else
1504                     phy->t_val[5] = 0 ; /* short */
1505 
1506                 /*
1507                  * Implementers choice: use medium
1508                  * instead of short when undesired
1509                  * connection attempt is made.
1510                  */
1511                 if (phy->wc_flag)
1512                     phy->t_val[5] = 1 ; /* medium */
1513             }
1514             mib->fddiPORTConnectState = PCM_CONNECTING ;
1515         }
1516         else {
1517             mib->fddiPORTConnectState = PCM_STANDBY ;
1518             phy->t_val[4] = 1 ; /* extended */
1519             phy->t_val[5] = 1 ;
1520         }
1521         break ;
1522     case 5:
1523         break ;
1524     case 6:
1525         /* we do NOT have a MAC for LCT */
1526         phy->t_val[6] = 0 ;
1527         break ;
1528     case 7:
1529         phy->cf_loop = FALSE ;
1530         lem_check_lct(smc,phy) ;
1531         if (phy->pc_lem_fail) {
1532             DB_PCMN(1,"PCM %c : E104 LCT failed\n",
1533                 phy->phy_name,0) ;
1534             phy->t_val[7] = 1 ;
1535         }
1536         else
1537             phy->t_val[7] = 0 ;
1538         break ;
1539     case 8:
1540         phy->t_val[8] = 0 ; /* Don't request MAC loopback */
1541         break ;
1542     case 9:
1543         phy->cf_loop = 0 ;
1544         if ((mib->fddiPORTPC_Withhold != PC_WH_NONE) ||
1545              ((smc->s.sas == SMT_DAS) && (phy->wc_flag))) {
1546             queue_event(smc,EVENT_PCM+np,PC_START) ;
1547             break ;
1548         }
1549         phy->t_val[9] = FALSE ;
1550         switch (smc->s.sas) {
1551         case SMT_DAS :
1552             /*
1553              * MAC intended on output
1554              */
1555             if (phy->pc_mode == PM_TREE) {
1556                 if ((np == PB) || ((np == PA) &&
1557                 (smc->y[PB].mib->fddiPORTConnectState !=
1558                     PCM_ACTIVE)))
1559                     phy->t_val[9] = TRUE ;
1560             }
1561             else {
1562                 if (np == PB)
1563                     phy->t_val[9] = TRUE ;
1564             }
1565             break ;
1566         case SMT_SAS :
1567             if (np == PS)
1568                 phy->t_val[9] = TRUE ;
1569             break ;
1570 #ifdef  CONCENTRATOR
1571         case SMT_NAC :
1572             /*
1573              * MAC intended on output
1574              */
1575             if (np == PB)
1576                 phy->t_val[9] = TRUE ;
1577             break ;
1578 #endif
1579         }
1580         mib->fddiPORTMacIndicated.T_val = phy->t_val[9] ;
1581         break ;
1582     }
1583     DB_PCMN(1,"SIG snd %x %x:\n", bit,phy->t_val[bit] ) ;
1584 }
1585 
1586 /*
1587  * return status twisted (called by SMT)
1588  */
1589 int pcm_status_twisted(struct s_smc *smc)
1590 {
1591     int twist = 0 ;
1592     if (smc->s.sas != SMT_DAS)
1593         return 0;
1594     if (smc->y[PA].twisted && (smc->y[PA].mib->fddiPORTPCMState == PC8_ACTIVE))
1595         twist |= 1 ;
1596     if (smc->y[PB].twisted && (smc->y[PB].mib->fddiPORTPCMState == PC8_ACTIVE))
1597         twist |= 2 ;
1598     return twist;
1599 }
1600 
1601 /*
1602  * return status    (called by SMT)
1603  *  type
1604  *  state
1605  *  remote phy type
1606  *  remote mac yes/no
1607  */
1608 void pcm_status_state(struct s_smc *smc, int np, int *type, int *state,
1609               int *remote, int *mac)
1610 {
1611     struct s_phy    *phy = &smc->y[np] ;
1612     struct fddi_mib_p   *mib ;
1613 
1614     mib = phy->mib ;
1615 
1616     /* remote PHY type and MAC - set only if active */
1617     *mac = 0 ;
1618     *type = mib->fddiPORTMy_Type ;      /* our PHY type */
1619     *state = mib->fddiPORTConnectState ;
1620     *remote = mib->fddiPORTNeighborType ;
1621 
1622     switch(mib->fddiPORTPCMState) {
1623     case PC8_ACTIVE :
1624         *mac = mib->fddiPORTMacIndicated.R_val ;
1625         break ;
1626     }
1627 }
1628 
1629 /*
1630  * return rooted station status (called by SMT)
1631  */
1632 int pcm_rooted_station(struct s_smc *smc)
1633 {
1634     int n ;
1635 
1636     for (n = 0 ; n < NUMPHYS ; n++) {
1637         if (smc->y[n].mib->fddiPORTPCMState == PC8_ACTIVE &&
1638             smc->y[n].mib->fddiPORTNeighborType == TM)
1639             return 0;
1640     }
1641     return 1;
1642 }
1643 
1644 /*
1645  * Interrupt actions for PLC & PCM events
1646  */
1647 void plc_irq(struct s_smc *smc, int np, unsigned int cmd)
1648 /* int np;  PHY index */
1649 {
1650     struct s_phy *phy = &smc->y[np] ;
1651     struct s_plc *plc = &phy->plc ;
1652     int     n ;
1653 #ifdef  SUPERNET_3
1654     int     corr_mask ;
1655 #endif  /* SUPERNET_3 */
1656     int     i ;
1657 
1658     if (np >= smc->s.numphys) {
1659         plc->soft_err++ ;
1660         return ;
1661     }
1662     if (cmd & PL_EBUF_ERR) {    /* elastic buff. det. over-|underflow*/
1663         /*
1664          * Check whether the SRF Condition occurred.
1665          */
1666         if (!plc->ebuf_cont && phy->mib->fddiPORTPCMState == PC8_ACTIVE){
1667             /*
1668              * This is the real Elasticity Error.
1669              * More than one in a row are treated as a
1670              * single one.
1671              * Only count this in the active state.
1672              */
1673             phy->mib->fddiPORTEBError_Ct ++ ;
1674 
1675         }
1676 
1677         plc->ebuf_err++ ;
1678         if (plc->ebuf_cont <= 1000) {
1679             /*
1680              * Prevent counter from being wrapped after
1681              * hanging years in that interrupt.
1682              */
1683             plc->ebuf_cont++ ;  /* Ebuf continuous error */
1684         }
1685 
1686 #ifdef  SUPERNET_3
1687         if (plc->ebuf_cont == 1000 &&
1688             ((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) ==
1689             PLC_REV_SN3)) {
1690             /*
1691              * This interrupt remeained high for at least
1692              * 1000 consecutive interrupt calls.
1693              *
1694              * This is caused by a hardware error of the
1695              * ORION part of the Supernet III chipset.
1696              *
1697              * Disable this bit from the mask.
1698              */
1699             corr_mask = (plc_imsk_na & ~PL_EBUF_ERR) ;
1700             outpw(PLC(np,PL_INTR_MASK),corr_mask);
1701 
1702             /*
1703              * Disconnect from the ring.
1704              * Call the driver with the reset indication.
1705              */
1706             queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
1707 
1708             /*
1709              * Make an error log entry.
1710              */
1711             SMT_ERR_LOG(smc,SMT_E0136, SMT_E0136_MSG) ;
1712 
1713             /*
1714              * Indicate the Reset.
1715              */
1716             drv_reset_indication(smc) ;
1717         }
1718 #endif  /* SUPERNET_3 */
1719     } else {
1720         /* Reset the continuous error variable */
1721         plc->ebuf_cont = 0 ;    /* reset Ebuf continuous error */
1722     }
1723     if (cmd & PL_PHYINV) {      /* physical layer invalid signal */
1724         plc->phyinv++ ;
1725     }
1726     if (cmd & PL_VSYM_CTR) {    /* violation symbol counter has incr.*/
1727         plc->vsym_ctr++ ;
1728     }
1729     if (cmd & PL_MINI_CTR) {    /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
1730         plc->mini_ctr++ ;
1731     }
1732     if (cmd & PL_LE_CTR) {      /* link error event counter */
1733         int j ;
1734 
1735         /*
1736          * note: PL_LINK_ERR_CTR MUST be read to clear it
1737          */
1738         j = inpw(PLC(np,PL_LE_THRESHOLD)) ;
1739         i = inpw(PLC(np,PL_LINK_ERR_CTR)) ;
1740 
1741         if (i < j) {
1742             /* wrapped around */
1743             i += 256 ;
1744         }
1745 
1746         if (phy->lem.lem_on) {
1747             /* Note: Lem errors shall only be counted when
1748              * link is ACTIVE or LCT is active.
1749              */
1750             phy->lem.lem_errors += i ;
1751             phy->mib->fddiPORTLem_Ct += i ;
1752         }
1753     }
1754     if (cmd & PL_TPC_EXPIRED) { /* TPC timer reached zero */
1755         if (plc->p_state == PS_LCT) {
1756             /*
1757              * end of LCT
1758              */
1759             ;
1760         }
1761         plc->tpc_exp++ ;
1762     }
1763     if (cmd & PL_LS_MATCH) {    /* LS == LS in PLC_CNTRL_B's MATCH_LS*/
1764         switch (inpw(PLC(np,PL_CNTRL_B)) & PL_MATCH_LS) {
1765         case PL_I_IDLE :    phy->curr_ls = PC_ILS ;     break ;
1766         case PL_I_HALT :    phy->curr_ls = PC_HLS ;     break ;
1767         case PL_I_MASTR :   phy->curr_ls = PC_MLS ;     break ;
1768         case PL_I_QUIET :   phy->curr_ls = PC_QLS ;     break ;
1769         }
1770     }
1771     if (cmd & PL_PCM_BREAK) {   /* PCM has entered the BREAK state */
1772         int reason;
1773 
1774         reason = inpw(PLC(np,PL_STATUS_B)) & PL_BREAK_REASON ;
1775 
1776         switch (reason) {
1777         case PL_B_PCS :     plc->b_pcs++ ;  break ;
1778         case PL_B_TPC :     plc->b_tpc++ ;  break ;
1779         case PL_B_TNE :     plc->b_tne++ ;  break ;
1780         case PL_B_QLS :     plc->b_qls++ ;  break ;
1781         case PL_B_ILS :     plc->b_ils++ ;  break ;
1782         case PL_B_HLS :     plc->b_hls++ ;  break ;
1783         }
1784 
1785         /*jd 05-Aug-1999 changed: Bug #10419 */
1786         DB_PCMN(1,"PLC %d: MDcF = %x\n", np, smc->e.DisconnectFlag);
1787         if (smc->e.DisconnectFlag == FALSE) {
1788             DB_PCMN(1,"PLC %d: restart (reason %x)\n", np, reason);
1789             queue_event(smc,EVENT_PCM+np,PC_START) ;
1790         }
1791         else {
1792             DB_PCMN(1,"PLC %d: NO!! restart (reason %x)\n", np, reason);
1793         }
1794         return ;
1795     }
1796     /*
1797      * If both CODE & ENABLE are set ignore enable
1798      */
1799     if (cmd & PL_PCM_CODE) { /* receive last sign.-bit | LCT complete */
1800         queue_event(smc,EVENT_PCM+np,PC_SIGNAL) ;
1801         n = inpw(PLC(np,PL_RCV_VECTOR)) ;
1802         for (i = 0 ; i < plc->p_bits ; i++) {
1803             phy->r_val[plc->p_start+i] = n & 1 ;
1804             n >>= 1 ;
1805         }
1806     }
1807     else if (cmd & PL_PCM_ENABLED) { /* asserted SC_JOIN, scrub.completed*/
1808         queue_event(smc,EVENT_PCM+np,PC_JOIN) ;
1809     }
1810     if (cmd & PL_TRACE_PROP) {  /* MLS while PC8_ACTIV || PC2_TRACE */
1811         /*PC22b*/
1812         if (!phy->tr_flag) {
1813             DB_PCMN(1,"PCM : irq TRACE_PROP %d %d\n",
1814                 np,smc->mib.fddiSMTECMState) ;
1815             phy->tr_flag = TRUE ;
1816             smc->e.trace_prop |= ENTITY_BIT(ENTITY_PHY(np)) ;
1817             queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ;
1818         }
1819     }
1820     /*
1821      * filter PLC glitch ???
1822      * QLS || HLS only while in PC2_TRACE state
1823      */
1824     if ((cmd & PL_SELF_TEST) && (phy->mib->fddiPORTPCMState == PC2_TRACE)) {
1825         /*PC22a*/
1826         if (smc->e.path_test == PT_PASSED) {
1827             DB_PCMN(1,"PCM : state = %s %d\n", get_pcmstate(smc,np),
1828                 phy->mib->fddiPORTPCMState) ;
1829 
1830             smc->e.path_test = PT_PENDING ;
1831             queue_event(smc,EVENT_ECM,EC_PATH_TEST) ;
1832         }
1833     }
1834     if (cmd & PL_TNE_EXPIRED) { /* TNE: length of noise events */
1835         /* break_required (TNE > NS_Max) */
1836         if (phy->mib->fddiPORTPCMState == PC8_ACTIVE) {
1837             if (!phy->tr_flag) {
1838                DB_PCMN(1,"PCM %c : PC81 %s\n",phy->phy_name,"NSE");
1839                queue_event(smc,EVENT_PCM+np,PC_START) ;
1840                return ;
1841             }
1842         }
1843     }
1844 #if 0
1845     if (cmd & PL_NP_ERR) {      /* NP has requested to r/w an inv reg*/
1846         /*
1847          * It's a bug by AMD
1848          */
1849         plc->np_err++ ;
1850     }
1851     /* pin inactiv (GND) */
1852     if (cmd & PL_PARITY_ERR) {  /* p. error dedected on TX9-0 inp */
1853         plc->parity_err++ ;
1854     }
1855     if (cmd & PL_LSDO) {        /* carrier detected */
1856         ;
1857     }
1858 #endif
1859 }
1860 
1861 #ifdef  DEBUG
1862 /*
1863  * fill state struct
1864  */
1865 void pcm_get_state(struct s_smc *smc, struct smt_state *state)
1866 {
1867     struct s_phy    *phy ;
1868     struct pcm_state *pcs ;
1869     int i ;
1870     int ii ;
1871     short   rbits ;
1872     short   tbits ;
1873     struct fddi_mib_p   *mib ;
1874 
1875     for (i = 0, phy = smc->y, pcs = state->pcm_state ; i < NUMPHYS ;
1876         i++ , phy++, pcs++ ) {
1877         mib = phy->mib ;
1878         pcs->pcm_type = (u_char) mib->fddiPORTMy_Type ;
1879         pcs->pcm_state = (u_char) mib->fddiPORTPCMState ;
1880         pcs->pcm_mode = phy->pc_mode ;
1881         pcs->pcm_neighbor = (u_char) mib->fddiPORTNeighborType ;
1882         pcs->pcm_bsf = mib->fddiPORTBS_Flag ;
1883         pcs->pcm_lsf = phy->ls_flag ;
1884         pcs->pcm_lct_fail = (u_char) mib->fddiPORTLCTFail_Ct ;
1885         pcs->pcm_ls_rx = LS2MIB(sm_pm_get_ls(smc,i)) ;
1886         for (ii = 0, rbits = tbits = 0 ; ii < NUMBITS ; ii++) {
1887             rbits <<= 1 ;
1888             tbits <<= 1 ;
1889             if (phy->r_val[NUMBITS-1-ii])
1890                 rbits |= 1 ;
1891             if (phy->t_val[NUMBITS-1-ii])
1892                 tbits |= 1 ;
1893         }
1894         pcs->pcm_r_val = rbits ;
1895         pcs->pcm_t_val = tbits ;
1896     }
1897 }
1898 
1899 int get_pcm_state(struct s_smc *smc, int np)
1900 {
1901     int pcs ;
1902 
1903     SK_UNUSED(smc) ;
1904 
1905     switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
1906         case PL_PC0 :   pcs = PC_STOP ;     break ;
1907         case PL_PC1 :   pcs = PC_START ;    break ;
1908         case PL_PC2 :   pcs = PC_TRACE ;    break ;
1909         case PL_PC3 :   pcs = PC_SIGNAL ;   break ;
1910         case PL_PC4 :   pcs = PC_SIGNAL ;   break ;
1911         case PL_PC5 :   pcs = PC_SIGNAL ;   break ;
1912         case PL_PC6 :   pcs = PC_JOIN ;     break ;
1913         case PL_PC7 :   pcs = PC_JOIN ;     break ;
1914         case PL_PC8 :   pcs = PC_ENABLE ;   break ;
1915         case PL_PC9 :   pcs = PC_MAINT ;    break ;
1916         default :   pcs = PC_DISABLE ;  break ;
1917     }
1918     return pcs;
1919 }
1920 
1921 char *get_linestate(struct s_smc *smc, int np)
1922 {
1923     char *ls = "" ;
1924 
1925     SK_UNUSED(smc) ;
1926 
1927     switch (inpw(PLC(np,PL_STATUS_A)) & PL_LINE_ST) {
1928         case PL_L_NLS : ls = "NOISE" ;  break ;
1929         case PL_L_ALS : ls = "ACTIV" ;  break ;
1930         case PL_L_UND : ls = "UNDEF" ;  break ;
1931         case PL_L_ILS4: ls = "ILS 4" ;  break ;
1932         case PL_L_QLS : ls = "QLS" ;    break ;
1933         case PL_L_MLS : ls = "MLS" ;    break ;
1934         case PL_L_HLS : ls = "HLS" ;    break ;
1935         case PL_L_ILS16:ls = "ILS16" ;  break ;
1936 #ifdef  lint
1937         default:    ls = "unknown" ; break ;
1938 #endif
1939     }
1940     return ls;
1941 }
1942 
1943 char *get_pcmstate(struct s_smc *smc, int np)
1944 {
1945     char *pcs ;
1946     
1947     SK_UNUSED(smc) ;
1948 
1949     switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
1950         case PL_PC0 :   pcs = "OFF" ;       break ;
1951         case PL_PC1 :   pcs = "BREAK" ;     break ;
1952         case PL_PC2 :   pcs = "TRACE" ;     break ;
1953         case PL_PC3 :   pcs = "CONNECT";    break ;
1954         case PL_PC4 :   pcs = "NEXT" ;      break ;
1955         case PL_PC5 :   pcs = "SIGNAL" ;    break ;
1956         case PL_PC6 :   pcs = "JOIN" ;      break ;
1957         case PL_PC7 :   pcs = "VERIFY" ;    break ;
1958         case PL_PC8 :   pcs = "ACTIV" ;     break ;
1959         case PL_PC9 :   pcs = "MAINT" ;     break ;
1960         default :   pcs = "UNKNOWN" ;   break ;
1961     }
1962     return pcs;
1963 }
1964 
1965 void list_phy(struct s_smc *smc)
1966 {
1967     struct s_plc *plc ;
1968     int np ;
1969 
1970     for (np = 0 ; np < NUMPHYS ; np++) {
1971         plc  = &smc->y[np].plc ;
1972         printf("PHY %d:\tERRORS\t\t\tBREAK_REASONS\t\tSTATES:\n",np) ;
1973         printf("\tsoft_error: %ld \t\tPC_Start : %ld\n",
1974                         plc->soft_err,plc->b_pcs);
1975         printf("\tparity_err: %ld \t\tTPC exp. : %ld\t\tLine: %s\n",
1976             plc->parity_err,plc->b_tpc,get_linestate(smc,np)) ;
1977         printf("\tebuf_error: %ld \t\tTNE exp. : %ld\n",
1978                         plc->ebuf_err,plc->b_tne) ;
1979         printf("\tphyinvalid: %ld \t\tQLS det. : %ld\t\tPCM : %s\n",
1980             plc->phyinv,plc->b_qls,get_pcmstate(smc,np)) ;
1981         printf("\tviosym_ctr: %ld \t\tILS det. : %ld\n",
1982                         plc->vsym_ctr,plc->b_ils)  ;
1983         printf("\tmingap_ctr: %ld \t\tHLS det. : %ld\n",
1984                         plc->mini_ctr,plc->b_hls) ;
1985         printf("\tnodepr_err: %ld\n",plc->np_err) ;
1986         printf("\tTPC_exp : %ld\n",plc->tpc_exp) ;
1987         printf("\tLEM_err : %ld\n",smc->y[np].lem.lem_errors) ;
1988     }
1989 }
1990 
1991 
1992 #ifdef  CONCENTRATOR
1993 void pcm_lem_dump(struct s_smc *smc)
1994 {
1995     int     i ;
1996     struct s_phy    *phy ;
1997     struct fddi_mib_p   *mib ;
1998 
1999     char        *entostring() ;
2000 
2001     printf("PHY errors  BER\n") ;
2002     printf("----------------------\n") ;
2003     for (i = 0,phy = smc->y ; i < NUMPHYS ; i++,phy++) {
2004         if (!plc_is_installed(smc,i))
2005             continue ;
2006         mib = phy->mib ;
2007         printf("%s\t%ld\t10E-%d\n",
2008             entostring(smc,ENTITY_PHY(i)),
2009             mib->fddiPORTLem_Ct,
2010             mib->fddiPORTLer_Estimate) ;
2011     }
2012 }
2013 #endif
2014 #endif