linux/drivers/net/fddi/skfp/pcmplc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/******************************************************************************
   3 *
   4 *      (C)Copyright 1998,1999 SysKonnect,
   5 *      a business unit of Schneider & Koch & Co. Datensysteme GmbH.
   6 *
   7 *      See the file "skfddi.c" for further information.
   8 *
   9 *      The information in this file is provided "AS IS" without warranty.
  10 *
  11 ******************************************************************************/
  12
  13/*
  14        PCM
  15        Physical Connection Management
  16*/
  17
  18/*
  19 * Hardware independent state machine implemantation
  20 * The following external SMT functions are referenced :
  21 *
  22 *              queue_event()
  23 *              smt_timer_start()
  24 *              smt_timer_stop()
  25 *
  26 *      The following external HW dependent functions are referenced :
  27 *              sm_pm_control()
  28 *              sm_ph_linestate()
  29 *
  30 *      The following HW dependent events are required :
  31 *              PC_QLS
  32 *              PC_ILS
  33 *              PC_HLS
  34 *              PC_MLS
  35 *              PC_NSE
  36 *              PC_LEM
  37 *
  38 */
  39
  40
  41#include "h/types.h"
  42#include "h/fddi.h"
  43#include "h/smc.h"
  44#include "h/supern_2.h"
  45#define KERNEL
  46#include "h/smtstate.h"
  47
  48#ifndef lint
  49static const char ID_sccs[] = "@(#)pcmplc.c     2.55 99/08/05 (C) SK " ;
  50#endif
  51
  52#ifdef  FDDI_MIB
  53extern int snmp_fddi_trap(
  54#ifdef  ANSIC
  55struct s_smc    * smc, int  type, int  index
  56#endif
  57);
  58#endif
  59#ifdef  CONCENTRATOR
  60extern int plc_is_installed(
  61#ifdef  ANSIC
  62struct s_smc *smc ,
  63int p
  64#endif
  65) ;
  66#endif
  67/*
  68 * FSM Macros
  69 */
  70#define AFLAG           (0x20)
  71#define GO_STATE(x)     (mib->fddiPORTPCMState = (x)|AFLAG)
  72#define ACTIONS_DONE()  (mib->fddiPORTPCMState &= ~AFLAG)
  73#define ACTIONS(x)      (x|AFLAG)
  74
  75/*
  76 * PCM states
  77 */
  78#define PC0_OFF                 0
  79#define PC1_BREAK               1
  80#define PC2_TRACE               2
  81#define PC3_CONNECT             3
  82#define PC4_NEXT                4
  83#define PC5_SIGNAL              5
  84#define PC6_JOIN                6
  85#define PC7_VERIFY              7
  86#define PC8_ACTIVE              8
  87#define PC9_MAINT               9
  88
  89/*
  90 * symbolic state names
  91 */
  92static const char * const pcm_states[] =  {
  93        "PC0_OFF","PC1_BREAK","PC2_TRACE","PC3_CONNECT","PC4_NEXT",
  94        "PC5_SIGNAL","PC6_JOIN","PC7_VERIFY","PC8_ACTIVE","PC9_MAINT"
  95} ;
  96
  97/*
  98 * symbolic event names
  99 */
 100static const char * const pcm_events[] = {
 101        "NONE","PC_START","PC_STOP","PC_LOOP","PC_JOIN","PC_SIGNAL",
 102        "PC_REJECT","PC_MAINT","PC_TRACE","PC_PDR",
 103        "PC_ENABLE","PC_DISABLE",
 104        "PC_QLS","PC_ILS","PC_MLS","PC_HLS","PC_LS_PDR","PC_LS_NONE",
 105        "PC_TIMEOUT_TB_MAX","PC_TIMEOUT_TB_MIN",
 106        "PC_TIMEOUT_C_MIN","PC_TIMEOUT_T_OUT",
 107        "PC_TIMEOUT_TL_MIN","PC_TIMEOUT_T_NEXT","PC_TIMEOUT_LCT",
 108        "PC_NSE","PC_LEM"
 109} ;
 110
 111#ifdef  MOT_ELM
 112/*
 113 * PCL-S control register
 114 * this register in the PLC-S controls the scrambling parameters
 115 */
 116#define PLCS_CONTROL_C_U        0
 117#define PLCS_CONTROL_C_S        (PL_C_SDOFF_ENABLE | PL_C_SDON_ENABLE | \
 118                                 PL_C_CIPHER_ENABLE)
 119#define PLCS_FASSERT_U          0
 120#define PLCS_FASSERT_S          0xFd76  /* 52.0 us */
 121#define PLCS_FDEASSERT_U        0
 122#define PLCS_FDEASSERT_S        0
 123#else   /* nMOT_ELM */
 124/*
 125 * PCL-S control register
 126 * this register in the PLC-S controls the scrambling parameters
 127 * can be patched for ANSI compliance if standard changes
 128 */
 129static const u_char plcs_control_c_u[17] = "PLC_CNTRL_C_U=\0\0" ;
 130static const u_char plcs_control_c_s[17] = "PLC_CNTRL_C_S=\01\02" ;
 131
 132#define PLCS_CONTROL_C_U (plcs_control_c_u[14] | (plcs_control_c_u[15]<<8))
 133#define PLCS_CONTROL_C_S (plcs_control_c_s[14] | (plcs_control_c_s[15]<<8))
 134#endif  /* nMOT_ELM */
 135
 136/*
 137 * external vars
 138 */
 139/* struct definition see 'cmtdef.h' (also used by CFM) */
 140
 141#define PS_OFF          0
 142#define PS_BIT3         1
 143#define PS_BIT4         2
 144#define PS_BIT7         3
 145#define PS_LCT          4
 146#define PS_BIT8         5
 147#define PS_JOIN         6
 148#define PS_ACTIVE       7
 149
 150#define LCT_LEM_MAX     255
 151
 152/*
 153 * PLC timing parameter
 154 */
 155
 156#define PLC_MS(m)       ((int)((0x10000L-(m*100000L/2048))))
 157#define SLOW_TL_MIN     PLC_MS(6)
 158#define SLOW_C_MIN      PLC_MS(10)
 159
 160static  const struct plt {
 161        int     timer ;                 /* relative plc timer address */
 162        int     para ;                  /* default timing parameters */
 163} pltm[] = {
 164        { PL_C_MIN, SLOW_C_MIN },       /* min t. to remain Connect State */
 165        { PL_TL_MIN, SLOW_TL_MIN },     /* min t. to transmit a Line State */
 166        { PL_TB_MIN, TP_TB_MIN },       /* min break time */
 167        { PL_T_OUT, TP_T_OUT },         /* Signaling timeout */
 168        { PL_LC_LENGTH, TP_LC_LENGTH }, /* Link Confidence Test Time */
 169        { PL_T_SCRUB, TP_T_SCRUB },     /* Scrub Time == MAC TVX time ! */
 170        { PL_NS_MAX, TP_NS_MAX },       /* max t. that noise is tolerated */
 171        { 0,0 }
 172} ;
 173
 174/*
 175 * interrupt mask
 176 */
 177#ifdef  SUPERNET_3
 178/*
 179 * Do we need the EBUF error during signaling, too, to detect SUPERNET_3
 180 * PLL bug?
 181 */
 182static const int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
 183                        PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
 184#else   /* SUPERNET_3 */
 185/*
 186 * We do NOT need the elasticity buffer error during signaling.
 187 */
 188static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
 189                        PL_PCM_ENABLED | PL_SELF_TEST ;
 190#endif  /* SUPERNET_3 */
 191static const int plc_imsk_act = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
 192                        PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
 193
 194/* internal functions */
 195static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd);
 196static void pc_rcode_actions(struct s_smc *smc, int bit, struct s_phy *phy);
 197static void pc_tcode_actions(struct s_smc *smc, const int bit, struct s_phy *phy);
 198static void reset_lem_struct(struct s_phy *phy);
 199static void plc_init(struct s_smc *smc, int p);
 200static void sm_ph_lem_start(struct s_smc *smc, int np, int threshold);
 201static void sm_ph_lem_stop(struct s_smc *smc, int np);
 202static void sm_ph_linestate(struct s_smc *smc, int phy, int ls);
 203static void real_init_plc(struct s_smc *smc);
 204
 205/*
 206 * SMT timer interface
 207 *      start PCM timer 0
 208 */
 209static void start_pcm_timer0(struct s_smc *smc, u_long value, int event,
 210                             struct s_phy *phy)
 211{
 212        phy->timer0_exp = FALSE ;       /* clear timer event flag */
 213        smt_timer_start(smc,&phy->pcm_timer0,value,
 214                EV_TOKEN(EVENT_PCM+phy->np,event)) ;
 215}
 216/*
 217 * SMT timer interface
 218 *      stop PCM timer 0
 219 */
 220static void stop_pcm_timer0(struct s_smc *smc, struct s_phy *phy)
 221{
 222        if (phy->pcm_timer0.tm_active)
 223                smt_timer_stop(smc,&phy->pcm_timer0) ;
 224}
 225
 226/*
 227        init PCM state machine (called by driver)
 228        clear all PCM vars and flags
 229*/
 230void pcm_init(struct s_smc *smc)
 231{
 232        int             i ;
 233        int             np ;
 234        struct s_phy    *phy ;
 235        struct fddi_mib_p       *mib ;
 236
 237        for (np = 0,phy = smc->y ; np < NUMPHYS ; np++,phy++) {
 238                /* Indicates the type of PHY being used */
 239                mib = phy->mib ;
 240                mib->fddiPORTPCMState = ACTIONS(PC0_OFF) ;
 241                phy->np = np ;
 242                switch (smc->s.sas) {
 243#ifdef  CONCENTRATOR
 244                case SMT_SAS :
 245                        mib->fddiPORTMy_Type = (np == PS) ? TS : TM ;
 246                        break ;
 247                case SMT_DAS :
 248                        mib->fddiPORTMy_Type = (np == PA) ? TA :
 249                                        (np == PB) ? TB : TM ;
 250                        break ;
 251                case SMT_NAC :
 252                        mib->fddiPORTMy_Type = TM ;
 253                        break;
 254#else
 255                case SMT_SAS :
 256                        mib->fddiPORTMy_Type = (np == PS) ? TS : TNONE ;
 257                        mib->fddiPORTHardwarePresent = (np == PS) ? TRUE :
 258                                        FALSE ;
 259#ifndef SUPERNET_3
 260                        smc->y[PA].mib->fddiPORTPCMState = PC0_OFF ;
 261#else
 262                        smc->y[PB].mib->fddiPORTPCMState = PC0_OFF ;
 263#endif
 264                        break ;
 265                case SMT_DAS :
 266                        mib->fddiPORTMy_Type = (np == PB) ? TB : TA ;
 267                        break ;
 268#endif
 269                }
 270                /*
 271                 * set PMD-type
 272                 */
 273                phy->pmd_scramble = 0 ;
 274                switch (phy->pmd_type[PMD_SK_PMD]) {
 275                case 'P' :
 276                        mib->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ;
 277                        break ;
 278                case 'L' :
 279                        mib->fddiPORTPMDClass = MIB_PMDCLASS_LCF ;
 280                        break ;
 281                case 'D' :
 282                        mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
 283                        break ;
 284                case 'S' :
 285                        mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
 286                        phy->pmd_scramble = TRUE ;
 287                        break ;
 288                case 'U' :
 289                        mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
 290                        phy->pmd_scramble = TRUE ;
 291                        break ;
 292                case '1' :
 293                        mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
 294                        break ;
 295                case '2' :
 296                        mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
 297                        break ;
 298                case '3' :
 299                        mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ;
 300                        break ;
 301                case '4' :
 302                        mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ;
 303                        break ;
 304                case 'H' :
 305                        mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
 306                        break ;
 307                case 'I' :
 308                        mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
 309                        break ;
 310                case 'G' :
 311                        mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ;
 312                        break ;
 313                default:
 314                        mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ;
 315                        break ;
 316                }
 317                /*
 318                 * A and B port can be on primary and secondary path
 319                 */
 320                switch (mib->fddiPORTMy_Type) {
 321                case TA :
 322                        mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
 323                        mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
 324                        mib->fddiPORTRequestedPaths[2] =
 325                                MIB_P_PATH_LOCAL |
 326                                MIB_P_PATH_CON_ALTER |
 327                                MIB_P_PATH_SEC_PREFER ;
 328                        mib->fddiPORTRequestedPaths[3] =
 329                                MIB_P_PATH_LOCAL |
 330                                MIB_P_PATH_CON_ALTER |
 331                                MIB_P_PATH_SEC_PREFER |
 332                                MIB_P_PATH_THRU ;
 333                        break ;
 334                case TB :
 335                        mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
 336                        mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
 337                        mib->fddiPORTRequestedPaths[2] =
 338                                MIB_P_PATH_LOCAL |
 339                                MIB_P_PATH_PRIM_PREFER ;
 340                        mib->fddiPORTRequestedPaths[3] =
 341                                MIB_P_PATH_LOCAL |
 342                                MIB_P_PATH_PRIM_PREFER |
 343                                MIB_P_PATH_CON_PREFER |
 344                                MIB_P_PATH_THRU ;
 345                        break ;
 346                case TS :
 347                        mib->fddiPORTAvailablePaths |= MIB_PATH_S ;
 348                        mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
 349                        mib->fddiPORTRequestedPaths[2] =
 350                                MIB_P_PATH_LOCAL |
 351                                MIB_P_PATH_CON_ALTER |
 352                                MIB_P_PATH_PRIM_PREFER ;
 353                        mib->fddiPORTRequestedPaths[3] =
 354                                MIB_P_PATH_LOCAL |
 355                                MIB_P_PATH_CON_ALTER |
 356                                MIB_P_PATH_PRIM_PREFER ;
 357                        break ;
 358                case TM :
 359                        mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ;
 360                        mib->fddiPORTRequestedPaths[2] =
 361                                MIB_P_PATH_LOCAL |
 362                                MIB_P_PATH_SEC_ALTER |
 363                                MIB_P_PATH_PRIM_ALTER ;
 364                        mib->fddiPORTRequestedPaths[3] = 0 ;
 365                        break ;
 366                }
 367
 368                phy->pc_lem_fail = FALSE ;
 369                mib->fddiPORTPCMStateX = mib->fddiPORTPCMState ;
 370                mib->fddiPORTLCTFail_Ct = 0 ;
 371                mib->fddiPORTBS_Flag = 0 ;
 372                mib->fddiPORTCurrentPath = MIB_PATH_ISOLATED ;
 373                mib->fddiPORTNeighborType = TNONE ;
 374                phy->ls_flag = 0 ;
 375                phy->rc_flag = 0 ;
 376                phy->tc_flag = 0 ;
 377                phy->td_flag = 0 ;
 378                if (np >= PM)
 379                        phy->phy_name = '0' + np - PM ;
 380                else
 381                        phy->phy_name = 'A' + np ;
 382                phy->wc_flag = FALSE ;          /* set by SMT */
 383                memset((char *)&phy->lem,0,sizeof(struct lem_counter)) ;
 384                reset_lem_struct(phy) ;
 385                memset((char *)&phy->plc,0,sizeof(struct s_plc)) ;
 386                phy->plc.p_state = PS_OFF ;
 387                for (i = 0 ; i < NUMBITS ; i++) {
 388                        phy->t_next[i] = 0 ;
 389                }
 390        }
 391        real_init_plc(smc) ;
 392}
 393
 394void init_plc(struct s_smc *smc)
 395{
 396        SK_UNUSED(smc) ;
 397
 398        /*
 399         * dummy
 400         * this is an obsolete public entry point that has to remain
 401         * for compat. It is used by various drivers.
 402         * the work is now done in real_init_plc()
 403         * which is called from pcm_init() ;
 404         */
 405}
 406
 407static void real_init_plc(struct s_smc *smc)
 408{
 409        int     p ;
 410
 411        for (p = 0 ; p < NUMPHYS ; p++)
 412                plc_init(smc,p) ;
 413}
 414
 415static void plc_init(struct s_smc *smc, int p)
 416{
 417        int     i ;
 418#ifndef MOT_ELM
 419        int     rev ;   /* Revision of PLC-x */
 420#endif  /* MOT_ELM */
 421
 422        /* transit PCM state machine to MAINT state */
 423        outpw(PLC(p,PL_CNTRL_B),0) ;
 424        outpw(PLC(p,PL_CNTRL_B),PL_PCM_STOP) ;
 425        outpw(PLC(p,PL_CNTRL_A),0) ;
 426
 427        /*
 428         * if PLC-S then set control register C
 429         */
 430#ifndef MOT_ELM
 431        rev = inpw(PLC(p,PL_STATUS_A)) & PLC_REV_MASK ;
 432        if (rev != PLC_REVISION_A)
 433#endif  /* MOT_ELM */
 434        {
 435                if (smc->y[p].pmd_scramble) {
 436                        outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_S) ;
 437#ifdef  MOT_ELM
 438                        outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_S) ;
 439                        outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_S) ;
 440#endif  /* MOT_ELM */
 441                }
 442                else {
 443                        outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_U) ;
 444#ifdef  MOT_ELM
 445                        outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_U) ;
 446                        outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_U) ;
 447#endif  /* MOT_ELM */
 448                }
 449        }
 450
 451        /*
 452         * set timer register
 453         */
 454        for ( i = 0 ; pltm[i].timer; i++)       /* set timer parameter reg */
 455                outpw(PLC(p,pltm[i].timer),pltm[i].para) ;
 456
 457        (void)inpw(PLC(p,PL_INTR_EVENT)) ;      /* clear interrupt event reg */
 458        plc_clear_irq(smc,p) ;
 459        outpw(PLC(p,PL_INTR_MASK),plc_imsk_na); /* enable non active irq's */
 460
 461        /*
 462         * if PCM is configured for class s, it will NOT go to the
 463         * REMOVE state if offline (page 3-36;)
 464         * in the concentrator, all inactive PHYS always must be in
 465         * the remove state
 466         * there's no real need to use this feature at all ..
 467         */
 468#ifndef CONCENTRATOR
 469        if ((smc->s.sas == SMT_SAS) && (p == PS)) {
 470                outpw(PLC(p,PL_CNTRL_B),PL_CLASS_S) ;
 471        }
 472#endif
 473}
 474
 475/*
 476 * control PCM state machine
 477 */
 478static void plc_go_state(struct s_smc *smc, int p, int state)
 479{
 480        HW_PTR port ;
 481        int val ;
 482
 483        SK_UNUSED(smc) ;
 484
 485        port = (HW_PTR) (PLC(p,PL_CNTRL_B)) ;
 486        val = inpw(port) & ~(PL_PCM_CNTRL | PL_MAINT) ;
 487        outpw(port,val) ;
 488        outpw(port,val | state) ;
 489}
 490
 491/*
 492 * read current line state (called by ECM & PCM)
 493 */
 494int sm_pm_get_ls(struct s_smc *smc, int phy)
 495{
 496        int     state ;
 497
 498#ifdef  CONCENTRATOR
 499        if (!plc_is_installed(smc,phy))
 500                return PC_QLS;
 501#endif
 502
 503        state = inpw(PLC(phy,PL_STATUS_A)) & PL_LINE_ST ;
 504        switch(state) {
 505        case PL_L_QLS:
 506                state = PC_QLS ;
 507                break ;
 508        case PL_L_MLS:
 509                state = PC_MLS ;
 510                break ;
 511        case PL_L_HLS:
 512                state = PC_HLS ;
 513                break ;
 514        case PL_L_ILS4:
 515        case PL_L_ILS16:
 516                state = PC_ILS ;
 517                break ;
 518        case PL_L_ALS:
 519                state = PC_LS_PDR ;
 520                break ;
 521        default :
 522                state = PC_LS_NONE ;
 523        }
 524        return state;
 525}
 526
 527static int plc_send_bits(struct s_smc *smc, struct s_phy *phy, int len)
 528{
 529        int np = phy->np ;              /* PHY index */
 530        int     n ;
 531        int     i ;
 532
 533        SK_UNUSED(smc) ;
 534
 535        /* create bit vector */
 536        for (i = len-1,n = 0 ; i >= 0 ; i--) {
 537                n = (n<<1) | phy->t_val[phy->bitn+i] ;
 538        }
 539        if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
 540#if     0
 541                printf("PL_PCM_SIGNAL is set\n") ;
 542#endif
 543                return 1;
 544        }
 545        /* write bit[n] & length = 1 to regs */
 546        outpw(PLC(np,PL_VECTOR_LEN),len-1) ;    /* len=nr-1 */
 547        outpw(PLC(np,PL_XMIT_VECTOR),n) ;
 548#ifdef  DEBUG
 549#if 1
 550#ifdef  DEBUG_BRD
 551        if (smc->debug.d_plc & 0x80)
 552#else
 553        if (debug.d_plc & 0x80)
 554#endif
 555                printf("SIGNALING bit %d .. %d\n",phy->bitn,phy->bitn+len-1) ;
 556#endif
 557#endif
 558        return 0;
 559}
 560
 561/*
 562 * config plc muxes
 563 */
 564void plc_config_mux(struct s_smc *smc, int mux)
 565{
 566        if (smc->s.sas != SMT_DAS)
 567                return ;
 568        if (mux == MUX_WRAPB) {
 569                SETMASK(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
 570                SETMASK(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
 571        }
 572        else {
 573                CLEAR(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
 574                CLEAR(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP) ;
 575        }
 576        CLEAR(PLC(PB,PL_CNTRL_B),PL_CONFIG_CNTRL) ;
 577        CLEAR(PLC(PB,PL_CNTRL_A),PL_SC_REM_LOOP) ;
 578}
 579
 580/*
 581        PCM state machine
 582        called by dispatcher  & fddi_init() (driver)
 583        do
 584                display state change
 585                process event
 586        until SM is stable
 587*/
 588void pcm(struct s_smc *smc, const int np, int event)
 589{
 590        int     state ;
 591        int     oldstate ;
 592        struct s_phy    *phy ;
 593        struct fddi_mib_p       *mib ;
 594
 595#ifndef CONCENTRATOR
 596        /*
 597         * ignore 2nd PHY if SAS
 598         */
 599        if ((np != PS) && (smc->s.sas == SMT_SAS))
 600                return ;
 601#endif
 602        phy = &smc->y[np] ;
 603        mib = phy->mib ;
 604        oldstate = mib->fddiPORTPCMState ;
 605        do {
 606                DB_PCM("PCM %c: state %s%s, event %s",
 607                       phy->phy_name,
 608                       mib->fddiPORTPCMState & AFLAG ? "ACTIONS " : "",
 609                       pcm_states[mib->fddiPORTPCMState & ~AFLAG],
 610                       pcm_events[event]);
 611                state = mib->fddiPORTPCMState ;
 612                pcm_fsm(smc,phy,event) ;
 613                event = 0 ;
 614        } while (state != mib->fddiPORTPCMState) ;
 615        /*
 616         * because the PLC does the bit signaling for us,
 617         * we're always in SIGNAL state
 618         * the MIB want's to see CONNECT
 619         * we therefore fake an entry in the MIB
 620         */
 621        if (state == PC5_SIGNAL)
 622                mib->fddiPORTPCMStateX = PC3_CONNECT ;
 623        else
 624                mib->fddiPORTPCMStateX = state ;
 625
 626#ifndef SLIM_SMT
 627        /*
 628         * path change
 629         */
 630        if (    mib->fddiPORTPCMState != oldstate &&
 631                ((oldstate == PC8_ACTIVE) || (mib->fddiPORTPCMState == PC8_ACTIVE))) {
 632                smt_srf_event(smc,SMT_EVENT_PORT_PATH_CHANGE,
 633                        (int) (INDEX_PORT+ phy->np),0) ;
 634        }
 635#endif
 636
 637#ifdef FDDI_MIB
 638        /* check whether a snmp-trap has to be sent */
 639
 640        if ( mib->fddiPORTPCMState != oldstate ) {
 641                /* a real state change took place */
 642                DB_SNMP ("PCM from %d to %d\n", oldstate, mib->fddiPORTPCMState);
 643                if ( mib->fddiPORTPCMState == PC0_OFF ) {
 644                        /* send first trap */
 645                        snmp_fddi_trap (smc, 1, (int) mib->fddiPORTIndex );
 646                } else if ( oldstate == PC0_OFF ) {
 647                        /* send second trap */
 648                        snmp_fddi_trap (smc, 2, (int) mib->fddiPORTIndex );
 649                } else if ( mib->fddiPORTPCMState != PC2_TRACE &&
 650                        oldstate == PC8_ACTIVE ) {
 651                        /* send third trap */
 652                        snmp_fddi_trap (smc, 3, (int) mib->fddiPORTIndex );
 653                } else if ( mib->fddiPORTPCMState == PC8_ACTIVE ) {
 654                        /* send fourth trap */
 655                        snmp_fddi_trap (smc, 4, (int) mib->fddiPORTIndex );
 656                }
 657        }
 658#endif
 659
 660        pcm_state_change(smc,np,state) ;
 661}
 662
 663/*
 664 * PCM state machine
 665 */
 666static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd)
 667{
 668        int     i ;
 669        int     np = phy->np ;          /* PHY index */
 670        struct s_plc    *plc ;
 671        struct fddi_mib_p       *mib ;
 672#ifndef MOT_ELM
 673        u_short plc_rev ;               /* Revision of the plc */
 674#endif  /* nMOT_ELM */
 675
 676        plc = &phy->plc ;
 677        mib = phy->mib ;
 678
 679        /*
 680         * general transitions independent of state
 681         */
 682        switch (cmd) {
 683        case PC_STOP :
 684                /*PC00-PC80*/
 685                if (mib->fddiPORTPCMState != PC9_MAINT) {
 686                        GO_STATE(PC0_OFF) ;
 687                        AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
 688                                FDDI_PORT_EVENT, (u_long) FDDI_PORT_STOP,
 689                                smt_get_port_event_word(smc));
 690                }
 691                return ;
 692        case PC_START :
 693                /*PC01-PC81*/
 694                if (mib->fddiPORTPCMState != PC9_MAINT)
 695                        GO_STATE(PC1_BREAK) ;
 696                return ;
 697        case PC_DISABLE :
 698                /* PC09-PC99 */
 699                GO_STATE(PC9_MAINT) ;
 700                AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
 701                        FDDI_PORT_EVENT, (u_long) FDDI_PORT_DISABLED,
 702                        smt_get_port_event_word(smc));
 703                return ;
 704        case PC_TIMEOUT_LCT :
 705                /* if long or extended LCT */
 706                stop_pcm_timer0(smc,phy) ;
 707                CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
 708                /* end of LCT is indicate by PCM_CODE (initiate PCM event) */
 709                return ;
 710        }
 711
 712        switch(mib->fddiPORTPCMState) {
 713        case ACTIONS(PC0_OFF) :
 714                stop_pcm_timer0(smc,phy) ;
 715                outpw(PLC(np,PL_CNTRL_A),0) ;
 716                CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
 717                CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
 718                sm_ph_lem_stop(smc,np) ;                /* disable LEM */
 719                phy->cf_loop = FALSE ;
 720                phy->cf_join = FALSE ;
 721                queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
 722                plc_go_state(smc,np,PL_PCM_STOP) ;
 723                mib->fddiPORTConnectState = PCM_DISABLED ;
 724                ACTIONS_DONE() ;
 725                break ;
 726        case PC0_OFF:
 727                /*PC09*/
 728                if (cmd == PC_MAINT) {
 729                        GO_STATE(PC9_MAINT) ;
 730                        break ;
 731                }
 732                break ;
 733        case ACTIONS(PC1_BREAK) :
 734                /* Stop the LCT timer if we came from Signal state */
 735                stop_pcm_timer0(smc,phy) ;
 736                ACTIONS_DONE() ;
 737                plc_go_state(smc,np,0) ;
 738                CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
 739                CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
 740                sm_ph_lem_stop(smc,np) ;                /* disable LEM */
 741                /*
 742                 * if vector is already loaded, go to OFF to clear PCM_SIGNAL
 743                 */
 744#if     0
 745                if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
 746                        plc_go_state(smc,np,PL_PCM_STOP) ;
 747                        /* TB_MIN ? */
 748                }
 749#endif
 750                /*
 751                 * Go to OFF state in any case.
 752                 */
 753                plc_go_state(smc,np,PL_PCM_STOP) ;
 754
 755                if (mib->fddiPORTPC_Withhold == PC_WH_NONE)
 756                        mib->fddiPORTConnectState = PCM_CONNECTING ;
 757                phy->cf_loop = FALSE ;
 758                phy->cf_join = FALSE ;
 759                queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
 760                phy->ls_flag = FALSE ;
 761                phy->pc_mode = PM_NONE ;        /* needed by CFM */
 762                phy->bitn = 0 ;                 /* bit signaling start bit */
 763                for (i = 0 ; i < 3 ; i++)
 764                        pc_tcode_actions(smc,i,phy) ;
 765
 766                /* Set the non-active interrupt mask register */
 767                outpw(PLC(np,PL_INTR_MASK),plc_imsk_na) ;
 768
 769                /*
 770                 * If the LCT was stopped. There might be a
 771                 * PCM_CODE interrupt event present.
 772                 * This must be cleared.
 773                 */
 774                (void)inpw(PLC(np,PL_INTR_EVENT)) ;
 775#ifndef MOT_ELM
 776                /* Get the plc revision for revision dependent code */
 777                plc_rev = inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK ;
 778
 779                if (plc_rev != PLC_REV_SN3)
 780#endif  /* MOT_ELM */
 781                {
 782                        /*
 783                         * No supernet III PLC, so set Xmit verctor and
 784                         * length BEFORE starting the state machine.
 785                         */
 786                        if (plc_send_bits(smc,phy,3)) {
 787                                return ;
 788                        }
 789                }
 790
 791                /*
 792                 * Now give the Start command.
 793                 * - The start command shall be done before setting the bits
 794                 *   to be signaled. (In PLC-S description and PLCS in SN3.
 795                 * - The start command shall be issued AFTER setting the
 796                 *   XMIT vector and the XMIT length register.
 797                 *
 798                 * We do it exactly according this specs for the old PLC and
 799                 * the new PLCS inside the SN3.
 800                 * For the usual PLCS we try it the way it is done for the
 801                 * old PLC and set the XMIT registers again, if the PLC is
 802                 * not in SIGNAL state. This is done according to an PLCS
 803                 * errata workaround.
 804                 */
 805
 806                plc_go_state(smc,np,PL_PCM_START) ;
 807
 808                /*
 809                 * workaround for PLC-S eng. sample errata
 810                 */
 811#ifdef  MOT_ELM
 812                if (!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
 813#else   /* nMOT_ELM */
 814                if (((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) !=
 815                        PLC_REVISION_A) &&
 816                        !(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL))
 817#endif  /* nMOT_ELM */
 818                {
 819                        /*
 820                         * Set register again (PLCS errata) or the first time
 821                         * (new SN3 PLCS).
 822                         */
 823                        (void) plc_send_bits(smc,phy,3) ;
 824                }
 825                /*
 826                 * end of workaround
 827                 */
 828
 829                GO_STATE(PC5_SIGNAL) ;
 830                plc->p_state = PS_BIT3 ;
 831                plc->p_bits = 3 ;
 832                plc->p_start = 0 ;
 833
 834                break ;
 835        case PC1_BREAK :
 836                break ;
 837        case ACTIONS(PC2_TRACE) :
 838                plc_go_state(smc,np,PL_PCM_TRACE) ;
 839                ACTIONS_DONE() ;
 840                break ;
 841        case PC2_TRACE :
 842                break ;
 843
 844        case PC3_CONNECT :      /* these states are done by hardware */
 845        case PC4_NEXT :
 846                break ;
 847
 848        case ACTIONS(PC5_SIGNAL) :
 849                ACTIONS_DONE() ;
 850                /* fall through */
 851        case PC5_SIGNAL :
 852                if ((cmd != PC_SIGNAL) && (cmd != PC_TIMEOUT_LCT))
 853                        break ;
 854                switch (plc->p_state) {
 855                case PS_BIT3 :
 856                        for (i = 0 ; i <= 2 ; i++)
 857                                pc_rcode_actions(smc,i,phy) ;
 858                        pc_tcode_actions(smc,3,phy) ;
 859                        plc->p_state = PS_BIT4 ;
 860                        plc->p_bits = 1 ;
 861                        plc->p_start = 3 ;
 862                        phy->bitn = 3 ;
 863                        if (plc_send_bits(smc,phy,1)) {
 864                                return ;
 865                        }
 866                        break ;
 867                case PS_BIT4 :
 868                        pc_rcode_actions(smc,3,phy) ;
 869                        for (i = 4 ; i <= 6 ; i++)
 870                                pc_tcode_actions(smc,i,phy) ;
 871                        plc->p_state = PS_BIT7 ;
 872                        plc->p_bits = 3 ;
 873                        plc->p_start = 4 ;
 874                        phy->bitn = 4 ;
 875                        if (plc_send_bits(smc,phy,3)) {
 876                                return ;
 877                        }
 878                        break ;
 879                case PS_BIT7 :
 880                        for (i = 3 ; i <= 6 ; i++)
 881                                pc_rcode_actions(smc,i,phy) ;
 882                        plc->p_state = PS_LCT ;
 883                        plc->p_bits = 0 ;
 884                        plc->p_start = 7 ;
 885                        phy->bitn = 7 ;
 886                sm_ph_lem_start(smc,np,(int)smc->s.lct_short) ; /* enable LEM */
 887                        /* start LCT */
 888                        i = inpw(PLC(np,PL_CNTRL_B)) & ~PL_PC_LOOP ;
 889                        outpw(PLC(np,PL_CNTRL_B),i) ;   /* must be cleared */
 890                        outpw(PLC(np,PL_CNTRL_B),i | PL_RLBP) ;
 891                        break ;
 892                case PS_LCT :
 893                        /* check for local LCT failure */
 894                        pc_tcode_actions(smc,7,phy) ;
 895                        /*
 896                         * set tval[7]
 897                         */
 898                        plc->p_state = PS_BIT8 ;
 899                        plc->p_bits = 1 ;
 900                        plc->p_start = 7 ;
 901                        phy->bitn = 7 ;
 902                        if (plc_send_bits(smc,phy,1)) {
 903                                return ;
 904                        }
 905                        break ;
 906                case PS_BIT8 :
 907                        /* check for remote LCT failure */
 908                        pc_rcode_actions(smc,7,phy) ;
 909                        if (phy->t_val[7] || phy->r_val[7]) {
 910                                plc_go_state(smc,np,PL_PCM_STOP) ;
 911                                GO_STATE(PC1_BREAK) ;
 912                                break ;
 913                        }
 914                        for (i = 8 ; i <= 9 ; i++)
 915                                pc_tcode_actions(smc,i,phy) ;
 916                        plc->p_state = PS_JOIN ;
 917                        plc->p_bits = 2 ;
 918                        plc->p_start = 8 ;
 919                        phy->bitn = 8 ;
 920                        if (plc_send_bits(smc,phy,2)) {
 921                                return ;
 922                        }
 923                        break ;
 924                case PS_JOIN :
 925                        for (i = 8 ; i <= 9 ; i++)
 926                                pc_rcode_actions(smc,i,phy) ;
 927                        plc->p_state = PS_ACTIVE ;
 928                        GO_STATE(PC6_JOIN) ;
 929                        break ;
 930                }
 931                break ;
 932
 933        case ACTIONS(PC6_JOIN) :
 934                /*
 935                 * prevent mux error when going from WRAP_A to WRAP_B
 936                 */
 937                if (smc->s.sas == SMT_DAS && np == PB &&
 938                        (smc->y[PA].pc_mode == PM_TREE ||
 939                         smc->y[PB].pc_mode == PM_TREE)) {
 940                        SETMASK(PLC(np,PL_CNTRL_A),
 941                                PL_SC_REM_LOOP,PL_SC_REM_LOOP) ;
 942                        SETMASK(PLC(np,PL_CNTRL_B),
 943                                PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ;
 944                }
 945                SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
 946                SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ;
 947                ACTIONS_DONE() ;
 948                cmd = 0 ;
 949                /* fall thru */
 950        case PC6_JOIN :
 951                switch (plc->p_state) {
 952                case PS_ACTIVE:
 953                        /*PC88b*/
 954                        if (!phy->cf_join) {
 955                                phy->cf_join = TRUE ;
 956                                queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
 957                        }
 958                        if (cmd == PC_JOIN)
 959                                GO_STATE(PC8_ACTIVE) ;
 960                        /*PC82*/
 961                        if (cmd == PC_TRACE) {
 962                                GO_STATE(PC2_TRACE) ;
 963                                break ;
 964                        }
 965                        break ;
 966                }
 967                break ;
 968
 969        case PC7_VERIFY :
 970                break ;
 971
 972        case ACTIONS(PC8_ACTIVE) :
 973                /*
 974                 * start LEM for SMT
 975                 */
 976                sm_ph_lem_start(smc,(int)phy->np,LCT_LEM_MAX) ;
 977
 978                phy->tr_flag = FALSE ;
 979                mib->fddiPORTConnectState = PCM_ACTIVE ;
 980
 981                /* Set the active interrupt mask register */
 982                outpw(PLC(np,PL_INTR_MASK),plc_imsk_act) ;
 983
 984                ACTIONS_DONE() ;
 985                break ;
 986        case PC8_ACTIVE :
 987                /*PC81 is done by PL_TNE_EXPIRED irq */
 988                /*PC82*/
 989                if (cmd == PC_TRACE) {
 990                        GO_STATE(PC2_TRACE) ;
 991                        break ;
 992                }
 993                /*PC88c: is done by TRACE_PROP irq */
 994
 995                break ;
 996        case ACTIONS(PC9_MAINT) :
 997                stop_pcm_timer0(smc,phy) ;
 998                CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
 999                CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
1000                CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ; /* disable LEM int. */
1001                sm_ph_lem_stop(smc,np) ;                /* disable LEM */
1002                phy->cf_loop = FALSE ;
1003                phy->cf_join = FALSE ;
1004                queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
1005                plc_go_state(smc,np,PL_PCM_STOP) ;
1006                mib->fddiPORTConnectState = PCM_DISABLED ;
1007                SETMASK(PLC(np,PL_CNTRL_B),PL_MAINT,PL_MAINT) ;
1008                sm_ph_linestate(smc,np,(int) MIB2LS(mib->fddiPORTMaint_LS)) ;
1009                outpw(PLC(np,PL_CNTRL_A),PL_SC_BYPASS) ;
1010                ACTIONS_DONE() ;
1011                break ;
1012        case PC9_MAINT :
1013                DB_PCMN(1, "PCM %c : MAINT", phy->phy_name);
1014                /*PC90*/
1015                if (cmd == PC_ENABLE) {
1016                        GO_STATE(PC0_OFF) ;
1017                        break ;
1018                }
1019                break ;
1020
1021        default:
1022                SMT_PANIC(smc,SMT_E0118, SMT_E0118_MSG) ;
1023                break ;
1024        }
1025}
1026
1027/*
1028 * force line state on a PHY output     (only in MAINT state)
1029 */
1030static void sm_ph_linestate(struct s_smc *smc, int phy, int ls)
1031{
1032        int     cntrl ;
1033
1034        SK_UNUSED(smc) ;
1035
1036        cntrl = (inpw(PLC(phy,PL_CNTRL_B)) & ~PL_MAINT_LS) |
1037                                                PL_PCM_STOP | PL_MAINT ;
1038        switch(ls) {
1039        case PC_QLS:            /* Force Quiet */
1040                cntrl |= PL_M_QUI0 ;
1041                break ;
1042        case PC_MLS:            /* Force Master */
1043                cntrl |= PL_M_MASTR ;
1044                break ;
1045        case PC_HLS:            /* Force Halt */
1046                cntrl |= PL_M_HALT ;
1047                break ;
1048        default :
1049        case PC_ILS:            /* Force Idle */
1050                cntrl |= PL_M_IDLE ;
1051                break ;
1052        case PC_LS_PDR:         /* Enable repeat filter */
1053                cntrl |= PL_M_TPDR ;
1054                break ;
1055        }
1056        outpw(PLC(phy,PL_CNTRL_B),cntrl) ;
1057}
1058
1059static void reset_lem_struct(struct s_phy *phy)
1060{
1061        struct lem_counter *lem = &phy->lem ;
1062
1063        phy->mib->fddiPORTLer_Estimate = 15 ;
1064        lem->lem_float_ber = 15 * 100 ;
1065}
1066
1067/*
1068 * link error monitor
1069 */
1070static void lem_evaluate(struct s_smc *smc, struct s_phy *phy)
1071{
1072        int ber ;
1073        u_long errors ;
1074        struct lem_counter *lem = &phy->lem ;
1075        struct fddi_mib_p       *mib ;
1076        int                     cond ;
1077
1078        mib = phy->mib ;
1079
1080        if (!lem->lem_on)
1081                return ;
1082
1083        errors = inpw(PLC(((int) phy->np),PL_LINK_ERR_CTR)) ;
1084        lem->lem_errors += errors ;
1085        mib->fddiPORTLem_Ct += errors ;
1086
1087        errors = lem->lem_errors ;
1088        /*
1089         * calculation is called on a intervall of 8 seconds
1090         *      -> this means, that one error in 8 sec. is one of 8*125*10E6
1091         *      the same as BER = 10E-9
1092         * Please note:
1093         *      -> 9 errors in 8 seconds mean:
1094         *         BER = 9 * 10E-9  and this is
1095         *          < 10E-8, so the limit of 10E-8 is not reached!
1096         */
1097
1098                if (!errors)            ber = 15 ;
1099        else    if (errors <= 9)        ber = 9 ;
1100        else    if (errors <= 99)       ber = 8 ;
1101        else    if (errors <= 999)      ber = 7 ;
1102        else    if (errors <= 9999)     ber = 6 ;
1103        else    if (errors <= 99999)    ber = 5 ;
1104        else    if (errors <= 999999)   ber = 4 ;
1105        else    if (errors <= 9999999)  ber = 3 ;
1106        else    if (errors <= 99999999) ber = 2 ;
1107        else    if (errors <= 999999999) ber = 1 ;
1108        else                            ber = 0 ;
1109
1110        /*
1111         * weighted average
1112         */
1113        ber *= 100 ;
1114        lem->lem_float_ber = lem->lem_float_ber * 7 + ber * 3 ;
1115        lem->lem_float_ber /= 10 ;
1116        mib->fddiPORTLer_Estimate = lem->lem_float_ber / 100 ;
1117        if (mib->fddiPORTLer_Estimate < 4) {
1118                mib->fddiPORTLer_Estimate = 4 ;
1119        }
1120
1121        if (lem->lem_errors) {
1122                DB_PCMN(1, "LEM %c :", phy->np == PB ? 'B' : 'A');
1123                DB_PCMN(1, "errors      : %ld", lem->lem_errors);
1124                DB_PCMN(1, "sum_errors  : %ld", mib->fddiPORTLem_Ct);
1125                DB_PCMN(1, "current BER : 10E-%d", ber / 100);
1126                DB_PCMN(1, "float BER   : 10E-(%d/100)", lem->lem_float_ber);
1127                DB_PCMN(1, "avg. BER    : 10E-%d", mib->fddiPORTLer_Estimate);
1128        }
1129
1130        lem->lem_errors = 0L ;
1131
1132#ifndef SLIM_SMT
1133        cond = (mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Alarm) ?
1134                TRUE : FALSE ;
1135#ifdef  SMT_EXT_CUTOFF
1136        smt_ler_alarm_check(smc,phy,cond) ;
1137#endif  /* nSMT_EXT_CUTOFF */
1138        if (cond != mib->fddiPORTLerFlag) {
1139                smt_srf_event(smc,SMT_COND_PORT_LER,
1140                        (int) (INDEX_PORT+ phy->np) ,cond) ;
1141        }
1142#endif
1143
1144        if (    mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Cutoff) {
1145                phy->pc_lem_fail = TRUE ;               /* flag */
1146                mib->fddiPORTLem_Reject_Ct++ ;
1147                /*
1148                 * "forgive 10e-2" if we cutoff so we can come
1149                 * up again ..
1150                 */
1151                lem->lem_float_ber += 2*100 ;
1152
1153                /*PC81b*/
1154#ifdef  CONCENTRATOR
1155                DB_PCMN(1, "PCM: LER cutoff on port %d cutoff %d",
1156                        phy->np, mib->fddiPORTLer_Cutoff);
1157#endif
1158#ifdef  SMT_EXT_CUTOFF
1159                smt_port_off_event(smc,phy->np);
1160#else   /* nSMT_EXT_CUTOFF */
1161                queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
1162#endif  /* nSMT_EXT_CUTOFF */
1163        }
1164}
1165
1166/*
1167 * called by SMT to calculate LEM bit error rate
1168 */
1169void sm_lem_evaluate(struct s_smc *smc)
1170{
1171        int np ;
1172
1173        for (np = 0 ; np < NUMPHYS ; np++)
1174                lem_evaluate(smc,&smc->y[np]) ;
1175}
1176
1177static void lem_check_lct(struct s_smc *smc, struct s_phy *phy)
1178{
1179        struct lem_counter      *lem = &phy->lem ;
1180        struct fddi_mib_p       *mib ;
1181        int errors ;
1182
1183        mib = phy->mib ;
1184
1185        phy->pc_lem_fail = FALSE ;              /* flag */
1186        errors = inpw(PLC(((int)phy->np),PL_LINK_ERR_CTR)) ;
1187        lem->lem_errors += errors ;
1188        mib->fddiPORTLem_Ct += errors ;
1189        if (lem->lem_errors) {
1190                switch(phy->lc_test) {
1191                case LC_SHORT:
1192                        if (lem->lem_errors >= smc->s.lct_short)
1193                                phy->pc_lem_fail = TRUE ;
1194                        break ;
1195                case LC_MEDIUM:
1196                        if (lem->lem_errors >= smc->s.lct_medium)
1197                                phy->pc_lem_fail = TRUE ;
1198                        break ;
1199                case LC_LONG:
1200                        if (lem->lem_errors >= smc->s.lct_long)
1201                                phy->pc_lem_fail = TRUE ;
1202                        break ;
1203                case LC_EXTENDED:
1204                        if (lem->lem_errors >= smc->s.lct_extended)
1205                                phy->pc_lem_fail = TRUE ;
1206                        break ;
1207                }
1208                DB_PCMN(1, " >>errors : %lu", lem->lem_errors);
1209        }
1210        if (phy->pc_lem_fail) {
1211                mib->fddiPORTLCTFail_Ct++ ;
1212                mib->fddiPORTLem_Reject_Ct++ ;
1213        }
1214        else
1215                mib->fddiPORTLCTFail_Ct = 0 ;
1216}
1217
1218/*
1219 * LEM functions
1220 */
1221static void sm_ph_lem_start(struct s_smc *smc, int np, int threshold)
1222{
1223        struct lem_counter *lem = &smc->y[np].lem ;
1224
1225        lem->lem_on = 1 ;
1226        lem->lem_errors = 0L ;
1227
1228        /* Do NOT reset mib->fddiPORTLer_Estimate here. It is called too
1229         * often.
1230         */
1231
1232        outpw(PLC(np,PL_LE_THRESHOLD),threshold) ;
1233        (void)inpw(PLC(np,PL_LINK_ERR_CTR)) ;   /* clear error counter */
1234
1235        /* enable LE INT */
1236        SETMASK(PLC(np,PL_INTR_MASK),PL_LE_CTR,PL_LE_CTR) ;
1237}
1238
1239static void sm_ph_lem_stop(struct s_smc *smc, int np)
1240{
1241        struct lem_counter *lem = &smc->y[np].lem ;
1242
1243        lem->lem_on = 0 ;
1244        CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ;
1245}
1246
1247/*
1248 * PCM pseudo code
1249 * receive actions are called AFTER the bit n is received,
1250 * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received
1251 */
1252
1253/*
1254 * PCM pseudo code 5.1 .. 6.1
1255 */
1256static void pc_rcode_actions(struct s_smc *smc, int bit, struct s_phy *phy)
1257{
1258        struct fddi_mib_p       *mib ;
1259
1260        mib = phy->mib ;
1261
1262        DB_PCMN(1, "SIG rec %x %x:", bit, phy->r_val[bit]);
1263        bit++ ;
1264
1265        switch(bit) {
1266        case 0:
1267        case 1:
1268        case 2:
1269                break ;
1270        case 3 :
1271                if (phy->r_val[1] == 0 && phy->r_val[2] == 0)
1272                        mib->fddiPORTNeighborType = TA ;
1273                else if (phy->r_val[1] == 0 && phy->r_val[2] == 1)
1274                        mib->fddiPORTNeighborType = TB ;
1275                else if (phy->r_val[1] == 1 && phy->r_val[2] == 0)
1276                        mib->fddiPORTNeighborType = TS ;
1277                else if (phy->r_val[1] == 1 && phy->r_val[2] == 1)
1278                        mib->fddiPORTNeighborType = TM ;
1279                break ;
1280        case 4:
1281                if (mib->fddiPORTMy_Type == TM &&
1282                        mib->fddiPORTNeighborType == TM) {
1283                        DB_PCMN(1, "PCM %c : E100 withhold M-M",
1284                                phy->phy_name);
1285                        mib->fddiPORTPC_Withhold = PC_WH_M_M ;
1286                        RS_SET(smc,RS_EVENT) ;
1287                }
1288                else if (phy->t_val[3] || phy->r_val[3]) {
1289                        mib->fddiPORTPC_Withhold = PC_WH_NONE ;
1290                        if (mib->fddiPORTMy_Type == TM ||
1291                            mib->fddiPORTNeighborType == TM)
1292                                phy->pc_mode = PM_TREE ;
1293                        else
1294                                phy->pc_mode = PM_PEER ;
1295
1296                        /* reevaluate the selection criteria (wc_flag) */
1297                        all_selection_criteria (smc);
1298
1299                        if (phy->wc_flag) {
1300                                mib->fddiPORTPC_Withhold = PC_WH_PATH ;
1301                        }
1302                }
1303                else {
1304                        mib->fddiPORTPC_Withhold = PC_WH_OTHER ;
1305                        RS_SET(smc,RS_EVENT) ;
1306                        DB_PCMN(1, "PCM %c : E101 withhold other",
1307                                phy->phy_name);
1308                }
1309                phy->twisted = ((mib->fddiPORTMy_Type != TS) &&
1310                                (mib->fddiPORTMy_Type != TM) &&
1311                                (mib->fddiPORTNeighborType ==
1312                                mib->fddiPORTMy_Type)) ;
1313                if (phy->twisted) {
1314                        DB_PCMN(1, "PCM %c : E102 !!! TWISTED !!!",
1315                                phy->phy_name);
1316                }
1317                break ;
1318        case 5 :
1319                break ;
1320        case 6:
1321                if (phy->t_val[4] || phy->r_val[4]) {
1322                        if ((phy->t_val[4] && phy->t_val[5]) ||
1323                            (phy->r_val[4] && phy->r_val[5]) )
1324                                phy->lc_test = LC_EXTENDED ;
1325                        else
1326                                phy->lc_test = LC_LONG ;
1327                }
1328                else if (phy->t_val[5] || phy->r_val[5])
1329                        phy->lc_test = LC_MEDIUM ;
1330                else
1331                        phy->lc_test = LC_SHORT ;
1332                switch (phy->lc_test) {
1333                case LC_SHORT :                         /* 50ms */
1334                        outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LENGTH ) ;
1335                        phy->t_next[7] = smc->s.pcm_lc_short ;
1336                        break ;
1337                case LC_MEDIUM :                        /* 500ms */
1338                        outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LONGLN ) ;
1339                        phy->t_next[7] = smc->s.pcm_lc_medium ;
1340                        break ;
1341                case LC_LONG :
1342                        SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
1343                        phy->t_next[7] = smc->s.pcm_lc_long ;
1344                        break ;
1345                case LC_EXTENDED :
1346                        SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ;
1347                        phy->t_next[7] = smc->s.pcm_lc_extended ;
1348                        break ;
1349                }
1350                if (phy->t_next[7] > smc->s.pcm_lc_medium) {
1351                        start_pcm_timer0(smc,phy->t_next[7],PC_TIMEOUT_LCT,phy);
1352                }
1353                DB_PCMN(1, "LCT timer = %ld us", phy->t_next[7]);
1354                phy->t_next[9] = smc->s.pcm_t_next_9 ;
1355                break ;
1356        case 7:
1357                if (phy->t_val[6]) {
1358                        phy->cf_loop = TRUE ;
1359                }
1360                phy->td_flag = TRUE ;
1361                break ;
1362        case 8:
1363                if (phy->t_val[7] || phy->r_val[7]) {
1364                        DB_PCMN(1, "PCM %c : E103 LCT fail %s",
1365                                phy->phy_name,
1366                                phy->t_val[7] ? "local" : "remote");
1367                        queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ;
1368                }
1369                break ;
1370        case 9:
1371                if (phy->t_val[8] || phy->r_val[8]) {
1372                        if (phy->t_val[8])
1373                                phy->cf_loop = TRUE ;
1374                        phy->td_flag = TRUE ;
1375                }
1376                break ;
1377        case 10:
1378                if (phy->r_val[9]) {
1379                        /* neighbor intends to have MAC on output */ ;
1380                        mib->fddiPORTMacIndicated.R_val = TRUE ;
1381                }
1382                else {
1383                        /* neighbor does not intend to have MAC on output */ ;
1384                        mib->fddiPORTMacIndicated.R_val = FALSE ;
1385                }
1386                break ;
1387        }
1388}
1389
1390/*
1391 * PCM pseudo code 5.1 .. 6.1
1392 */
1393static void pc_tcode_actions(struct s_smc *smc, const int bit, struct s_phy *phy)
1394{
1395        int     np = phy->np ;
1396        struct fddi_mib_p       *mib ;
1397
1398        mib = phy->mib ;
1399
1400        switch(bit) {
1401        case 0:
1402                phy->t_val[0] = 0 ;             /* no escape used */
1403                break ;
1404        case 1:
1405                if (mib->fddiPORTMy_Type == TS || mib->fddiPORTMy_Type == TM)
1406                        phy->t_val[1] = 1 ;
1407                else
1408                        phy->t_val[1] = 0 ;
1409                break ;
1410        case 2 :
1411                if (mib->fddiPORTMy_Type == TB || mib->fddiPORTMy_Type == TM)
1412                        phy->t_val[2] = 1 ;
1413                else
1414                        phy->t_val[2] = 0 ;
1415                break ;
1416        case 3:
1417                {
1418                int     type,ne ;
1419                int     policy ;
1420
1421                type = mib->fddiPORTMy_Type ;
1422                ne = mib->fddiPORTNeighborType ;
1423                policy = smc->mib.fddiSMTConnectionPolicy ;
1424
1425                phy->t_val[3] = 1 ;     /* Accept connection */
1426                switch (type) {
1427                case TA :
1428                        if (
1429                                ((policy & POLICY_AA) && ne == TA) ||
1430                                ((policy & POLICY_AB) && ne == TB) ||
1431                                ((policy & POLICY_AS) && ne == TS) ||
1432                                ((policy & POLICY_AM) && ne == TM) )
1433                                phy->t_val[3] = 0 ;     /* Reject */
1434                        break ;
1435                case TB :
1436                        if (
1437                                ((policy & POLICY_BA) && ne == TA) ||
1438                                ((policy & POLICY_BB) && ne == TB) ||
1439                                ((policy & POLICY_BS) && ne == TS) ||
1440                                ((policy & POLICY_BM) && ne == TM) )
1441                                phy->t_val[3] = 0 ;     /* Reject */
1442                        break ;
1443                case TS :
1444                        if (
1445                                ((policy & POLICY_SA) && ne == TA) ||
1446                                ((policy & POLICY_SB) && ne == TB) ||
1447                                ((policy & POLICY_SS) && ne == TS) ||
1448                                ((policy & POLICY_SM) && ne == TM) )
1449                                phy->t_val[3] = 0 ;     /* Reject */
1450                        break ;
1451                case TM :
1452                        if (    ne == TM ||
1453                                ((policy & POLICY_MA) && ne == TA) ||
1454                                ((policy & POLICY_MB) && ne == TB) ||
1455                                ((policy & POLICY_MS) && ne == TS) ||
1456                                ((policy & POLICY_MM) && ne == TM) )
1457                                phy->t_val[3] = 0 ;     /* Reject */
1458                        break ;
1459                }
1460#ifndef SLIM_SMT
1461                /*
1462                 * detect undesirable connection attempt event
1463                 */
1464                if (    (type == TA && ne == TA ) ||
1465                        (type == TA && ne == TS ) ||
1466                        (type == TB && ne == TB ) ||
1467                        (type == TB && ne == TS ) ||
1468                        (type == TS && ne == TA ) ||
1469                        (type == TS && ne == TB ) ) {
1470                        smt_srf_event(smc,SMT_EVENT_PORT_CONNECTION,
1471                                (int) (INDEX_PORT+ phy->np) ,0) ;
1472                }
1473#endif
1474                }
1475                break ;
1476        case 4:
1477                if (mib->fddiPORTPC_Withhold == PC_WH_NONE) {
1478                        if (phy->pc_lem_fail) {
1479                                phy->t_val[4] = 1 ;     /* long */
1480                                phy->t_val[5] = 0 ;
1481                        }
1482                        else {
1483                                phy->t_val[4] = 0 ;
1484                                if (mib->fddiPORTLCTFail_Ct > 0)
1485                                        phy->t_val[5] = 1 ;     /* medium */
1486                                else
1487                                        phy->t_val[5] = 0 ;     /* short */
1488
1489                                /*
1490                                 * Implementers choice: use medium
1491                                 * instead of short when undesired
1492                                 * connection attempt is made.
1493                                 */
1494                                if (phy->wc_flag)
1495                                        phy->t_val[5] = 1 ;     /* medium */
1496                        }
1497                        mib->fddiPORTConnectState = PCM_CONNECTING ;
1498                }
1499                else {
1500                        mib->fddiPORTConnectState = PCM_STANDBY ;
1501                        phy->t_val[4] = 1 ;     /* extended */
1502                        phy->t_val[5] = 1 ;
1503                }
1504                break ;
1505        case 5:
1506                break ;
1507        case 6:
1508                /* we do NOT have a MAC for LCT */
1509                phy->t_val[6] = 0 ;
1510                break ;
1511        case 7:
1512                phy->cf_loop = FALSE ;
1513                lem_check_lct(smc,phy) ;
1514                if (phy->pc_lem_fail) {
1515                        DB_PCMN(1, "PCM %c : E104 LCT failed", phy->phy_name);
1516                        phy->t_val[7] = 1 ;
1517                }
1518                else
1519                        phy->t_val[7] = 0 ;
1520                break ;
1521        case 8:
1522                phy->t_val[8] = 0 ;     /* Don't request MAC loopback */
1523                break ;
1524        case 9:
1525                phy->cf_loop = 0 ;
1526                if ((mib->fddiPORTPC_Withhold != PC_WH_NONE) ||
1527                     ((smc->s.sas == SMT_DAS) && (phy->wc_flag))) {
1528                        queue_event(smc,EVENT_PCM+np,PC_START) ;
1529                        break ;
1530                }
1531                phy->t_val[9] = FALSE ;
1532                switch (smc->s.sas) {
1533                case SMT_DAS :
1534                        /*
1535                         * MAC intended on output
1536                         */
1537                        if (phy->pc_mode == PM_TREE) {
1538                                if ((np == PB) || ((np == PA) &&
1539                                (smc->y[PB].mib->fddiPORTConnectState !=
1540                                        PCM_ACTIVE)))
1541                                        phy->t_val[9] = TRUE ;
1542                        }
1543                        else {
1544                                if (np == PB)
1545                                        phy->t_val[9] = TRUE ;
1546                        }
1547                        break ;
1548                case SMT_SAS :
1549                        if (np == PS)
1550                                phy->t_val[9] = TRUE ;
1551                        break ;
1552#ifdef  CONCENTRATOR
1553                case SMT_NAC :
1554                        /*
1555                         * MAC intended on output
1556                         */
1557                        if (np == PB)
1558                                phy->t_val[9] = TRUE ;
1559                        break ;
1560#endif
1561                }
1562                mib->fddiPORTMacIndicated.T_val = phy->t_val[9] ;
1563                break ;
1564        }
1565        DB_PCMN(1, "SIG snd %x %x:", bit, phy->t_val[bit]);
1566}
1567
1568/*
1569 * return status twisted (called by SMT)
1570 */
1571int pcm_status_twisted(struct s_smc *smc)
1572{
1573        int     twist = 0 ;
1574        if (smc->s.sas != SMT_DAS)
1575                return 0;
1576        if (smc->y[PA].twisted && (smc->y[PA].mib->fddiPORTPCMState == PC8_ACTIVE))
1577                twist |= 1 ;
1578        if (smc->y[PB].twisted && (smc->y[PB].mib->fddiPORTPCMState == PC8_ACTIVE))
1579                twist |= 2 ;
1580        return twist;
1581}
1582
1583/*
1584 * return status        (called by SMT)
1585 *      type
1586 *      state
1587 *      remote phy type
1588 *      remote mac yes/no
1589 */
1590void pcm_status_state(struct s_smc *smc, int np, int *type, int *state,
1591                      int *remote, int *mac)
1592{
1593        struct s_phy    *phy = &smc->y[np] ;
1594        struct fddi_mib_p       *mib ;
1595
1596        mib = phy->mib ;
1597
1598        /* remote PHY type and MAC - set only if active */
1599        *mac = 0 ;
1600        *type = mib->fddiPORTMy_Type ;          /* our PHY type */
1601        *state = mib->fddiPORTConnectState ;
1602        *remote = mib->fddiPORTNeighborType ;
1603
1604        switch(mib->fddiPORTPCMState) {
1605        case PC8_ACTIVE :
1606                *mac = mib->fddiPORTMacIndicated.R_val ;
1607                break ;
1608        }
1609}
1610
1611/*
1612 * return rooted station status (called by SMT)
1613 */
1614int pcm_rooted_station(struct s_smc *smc)
1615{
1616        int     n ;
1617
1618        for (n = 0 ; n < NUMPHYS ; n++) {
1619                if (smc->y[n].mib->fddiPORTPCMState == PC8_ACTIVE &&
1620                    smc->y[n].mib->fddiPORTNeighborType == TM)
1621                        return 0;
1622        }
1623        return 1;
1624}
1625
1626/*
1627 * Interrupt actions for PLC & PCM events
1628 */
1629void plc_irq(struct s_smc *smc, int np, unsigned int cmd)
1630/* int np;      PHY index */
1631{
1632        struct s_phy *phy = &smc->y[np] ;
1633        struct s_plc *plc = &phy->plc ;
1634        int             n ;
1635#ifdef  SUPERNET_3
1636        int             corr_mask ;
1637#endif  /* SUPERNET_3 */
1638        int             i ;
1639
1640        if (np >= smc->s.numphys) {
1641                plc->soft_err++ ;
1642                return ;
1643        }
1644        if (cmd & PL_EBUF_ERR) {        /* elastic buff. det. over-|underflow*/
1645                /*
1646                 * Check whether the SRF Condition occurred.
1647                 */
1648                if (!plc->ebuf_cont && phy->mib->fddiPORTPCMState == PC8_ACTIVE){
1649                        /*
1650                         * This is the real Elasticity Error.
1651                         * More than one in a row are treated as a
1652                         * single one.
1653                         * Only count this in the active state.
1654                         */
1655                        phy->mib->fddiPORTEBError_Ct ++ ;
1656
1657                }
1658
1659                plc->ebuf_err++ ;
1660                if (plc->ebuf_cont <= 1000) {
1661                        /*
1662                         * Prevent counter from being wrapped after
1663                         * hanging years in that interrupt.
1664                         */
1665                        plc->ebuf_cont++ ;      /* Ebuf continuous error */
1666                }
1667
1668#ifdef  SUPERNET_3
1669                if (plc->ebuf_cont == 1000 &&
1670                        ((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) ==
1671                        PLC_REV_SN3)) {
1672                        /*
1673                         * This interrupt remeained high for at least
1674                         * 1000 consecutive interrupt calls.
1675                         *
1676                         * This is caused by a hardware error of the
1677                         * ORION part of the Supernet III chipset.
1678                         *
1679                         * Disable this bit from the mask.
1680                         */
1681                        corr_mask = (plc_imsk_na & ~PL_EBUF_ERR) ;
1682                        outpw(PLC(np,PL_INTR_MASK),corr_mask);
1683
1684                        /*
1685                         * Disconnect from the ring.
1686                         * Call the driver with the reset indication.
1687                         */
1688                        queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
1689
1690                        /*
1691                         * Make an error log entry.
1692                         */
1693                        SMT_ERR_LOG(smc,SMT_E0136, SMT_E0136_MSG) ;
1694
1695                        /*
1696                         * Indicate the Reset.
1697                         */
1698                        drv_reset_indication(smc) ;
1699                }
1700#endif  /* SUPERNET_3 */
1701        } else {
1702                /* Reset the continuous error variable */
1703                plc->ebuf_cont = 0 ;    /* reset Ebuf continuous error */
1704        }
1705        if (cmd & PL_PHYINV) {          /* physical layer invalid signal */
1706                plc->phyinv++ ;
1707        }
1708        if (cmd & PL_VSYM_CTR) {        /* violation symbol counter has incr.*/
1709                plc->vsym_ctr++ ;
1710        }
1711        if (cmd & PL_MINI_CTR) {        /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
1712                plc->mini_ctr++ ;
1713        }
1714        if (cmd & PL_LE_CTR) {          /* link error event counter */
1715                int     j ;
1716
1717                /*
1718                 * note: PL_LINK_ERR_CTR MUST be read to clear it
1719                 */
1720                j = inpw(PLC(np,PL_LE_THRESHOLD)) ;
1721                i = inpw(PLC(np,PL_LINK_ERR_CTR)) ;
1722
1723                if (i < j) {
1724                        /* wrapped around */
1725                        i += 256 ;
1726                }
1727
1728                if (phy->lem.lem_on) {
1729                        /* Note: Lem errors shall only be counted when
1730                         * link is ACTIVE or LCT is active.
1731                         */
1732                        phy->lem.lem_errors += i ;
1733                        phy->mib->fddiPORTLem_Ct += i ;
1734                }
1735        }
1736        if (cmd & PL_TPC_EXPIRED) {     /* TPC timer reached zero */
1737                if (plc->p_state == PS_LCT) {
1738                        /*
1739                         * end of LCT
1740                         */
1741                        ;
1742                }
1743                plc->tpc_exp++ ;
1744        }
1745        if (cmd & PL_LS_MATCH) {        /* LS == LS in PLC_CNTRL_B's MATCH_LS*/
1746                switch (inpw(PLC(np,PL_CNTRL_B)) & PL_MATCH_LS) {
1747                case PL_I_IDLE :        phy->curr_ls = PC_ILS ;         break ;
1748                case PL_I_HALT :        phy->curr_ls = PC_HLS ;         break ;
1749                case PL_I_MASTR :       phy->curr_ls = PC_MLS ;         break ;
1750                case PL_I_QUIET :       phy->curr_ls = PC_QLS ;         break ;
1751                }
1752        }
1753        if (cmd & PL_PCM_BREAK) {       /* PCM has entered the BREAK state */
1754                int     reason;
1755
1756                reason = inpw(PLC(np,PL_STATUS_B)) & PL_BREAK_REASON ;
1757
1758                switch (reason) {
1759                case PL_B_PCS :         plc->b_pcs++ ;  break ;
1760                case PL_B_TPC :         plc->b_tpc++ ;  break ;
1761                case PL_B_TNE :         plc->b_tne++ ;  break ;
1762                case PL_B_QLS :         plc->b_qls++ ;  break ;
1763                case PL_B_ILS :         plc->b_ils++ ;  break ;
1764                case PL_B_HLS :         plc->b_hls++ ;  break ;
1765                }
1766
1767                /*jd 05-Aug-1999 changed: Bug #10419 */
1768                DB_PCMN(1, "PLC %d: MDcF = %x", np, smc->e.DisconnectFlag);
1769                if (smc->e.DisconnectFlag == FALSE) {
1770                        DB_PCMN(1, "PLC %d: restart (reason %x)", np, reason);
1771                        queue_event(smc,EVENT_PCM+np,PC_START) ;
1772                }
1773                else {
1774                        DB_PCMN(1, "PLC %d: NO!! restart (reason %x)",
1775                                np, reason);
1776                }
1777                return ;
1778        }
1779        /*
1780         * If both CODE & ENABLE are set ignore enable
1781         */
1782        if (cmd & PL_PCM_CODE) { /* receive last sign.-bit | LCT complete */
1783                queue_event(smc,EVENT_PCM+np,PC_SIGNAL) ;
1784                n = inpw(PLC(np,PL_RCV_VECTOR)) ;
1785                for (i = 0 ; i < plc->p_bits ; i++) {
1786                        phy->r_val[plc->p_start+i] = n & 1 ;
1787                        n >>= 1 ;
1788                }
1789        }
1790        else if (cmd & PL_PCM_ENABLED) { /* asserted SC_JOIN, scrub.completed*/
1791                queue_event(smc,EVENT_PCM+np,PC_JOIN) ;
1792        }
1793        if (cmd & PL_TRACE_PROP) {      /* MLS while PC8_ACTIV || PC2_TRACE */
1794                /*PC22b*/
1795                if (!phy->tr_flag) {
1796                        DB_PCMN(1, "PCM : irq TRACE_PROP %d %d",
1797                                np, smc->mib.fddiSMTECMState);
1798                        phy->tr_flag = TRUE ;
1799                        smc->e.trace_prop |= ENTITY_BIT(ENTITY_PHY(np)) ;
1800                        queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ;
1801                }
1802        }
1803        /*
1804         * filter PLC glitch ???
1805         * QLS || HLS only while in PC2_TRACE state
1806         */
1807        if ((cmd & PL_SELF_TEST) && (phy->mib->fddiPORTPCMState == PC2_TRACE)) {
1808                /*PC22a*/
1809                if (smc->e.path_test == PT_PASSED) {
1810                        DB_PCMN(1, "PCM : state = %s %d",
1811                                get_pcmstate(smc, np),
1812                                phy->mib->fddiPORTPCMState);
1813
1814                        smc->e.path_test = PT_PENDING ;
1815                        queue_event(smc,EVENT_ECM,EC_PATH_TEST) ;
1816                }
1817        }
1818        if (cmd & PL_TNE_EXPIRED) {     /* TNE: length of noise events */
1819                /* break_required (TNE > NS_Max) */
1820                if (phy->mib->fddiPORTPCMState == PC8_ACTIVE) {
1821                        if (!phy->tr_flag) {
1822                                DB_PCMN(1, "PCM %c : PC81 %s",
1823                                        phy->phy_name, "NSE");
1824                                queue_event(smc, EVENT_PCM + np, PC_START);
1825                                return;
1826                        }
1827                }
1828        }
1829#if     0
1830        if (cmd & PL_NP_ERR) {          /* NP has requested to r/w an inv reg*/
1831                /*
1832                 * It's a bug by AMD
1833                 */
1834                plc->np_err++ ;
1835        }
1836        /* pin inactiv (GND) */
1837        if (cmd & PL_PARITY_ERR) {      /* p. error dedected on TX9-0 inp */
1838                plc->parity_err++ ;
1839        }
1840        if (cmd & PL_LSDO) {            /* carrier detected */
1841                ;
1842        }
1843#endif
1844}
1845
1846#ifdef  DEBUG
1847/*
1848 * fill state struct
1849 */
1850void pcm_get_state(struct s_smc *smc, struct smt_state *state)
1851{
1852        struct s_phy    *phy ;
1853        struct pcm_state *pcs ;
1854        int     i ;
1855        int     ii ;
1856        short   rbits ;
1857        short   tbits ;
1858        struct fddi_mib_p       *mib ;
1859
1860        for (i = 0, phy = smc->y, pcs = state->pcm_state ; i < NUMPHYS ;
1861                i++ , phy++, pcs++ ) {
1862                mib = phy->mib ;
1863                pcs->pcm_type = (u_char) mib->fddiPORTMy_Type ;
1864                pcs->pcm_state = (u_char) mib->fddiPORTPCMState ;
1865                pcs->pcm_mode = phy->pc_mode ;
1866                pcs->pcm_neighbor = (u_char) mib->fddiPORTNeighborType ;
1867                pcs->pcm_bsf = mib->fddiPORTBS_Flag ;
1868                pcs->pcm_lsf = phy->ls_flag ;
1869                pcs->pcm_lct_fail = (u_char) mib->fddiPORTLCTFail_Ct ;
1870                pcs->pcm_ls_rx = LS2MIB(sm_pm_get_ls(smc,i)) ;
1871                for (ii = 0, rbits = tbits = 0 ; ii < NUMBITS ; ii++) {
1872                        rbits <<= 1 ;
1873                        tbits <<= 1 ;
1874                        if (phy->r_val[NUMBITS-1-ii])
1875                                rbits |= 1 ;
1876                        if (phy->t_val[NUMBITS-1-ii])
1877                                tbits |= 1 ;
1878                }
1879                pcs->pcm_r_val = rbits ;
1880                pcs->pcm_t_val = tbits ;
1881        }
1882}
1883
1884int get_pcm_state(struct s_smc *smc, int np)
1885{
1886        int pcs ;
1887
1888        SK_UNUSED(smc) ;
1889
1890        switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
1891                case PL_PC0 :   pcs = PC_STOP ;         break ;
1892                case PL_PC1 :   pcs = PC_START ;        break ;
1893                case PL_PC2 :   pcs = PC_TRACE ;        break ;
1894                case PL_PC3 :   pcs = PC_SIGNAL ;       break ;
1895                case PL_PC4 :   pcs = PC_SIGNAL ;       break ;
1896                case PL_PC5 :   pcs = PC_SIGNAL ;       break ;
1897                case PL_PC6 :   pcs = PC_JOIN ;         break ;
1898                case PL_PC7 :   pcs = PC_JOIN ;         break ;
1899                case PL_PC8 :   pcs = PC_ENABLE ;       break ;
1900                case PL_PC9 :   pcs = PC_MAINT ;        break ;
1901                default :       pcs = PC_DISABLE ;      break ;
1902        }
1903        return pcs;
1904}
1905
1906char *get_linestate(struct s_smc *smc, int np)
1907{
1908        char *ls = "" ;
1909
1910        SK_UNUSED(smc) ;
1911
1912        switch (inpw(PLC(np,PL_STATUS_A)) & PL_LINE_ST) {
1913                case PL_L_NLS : ls = "NOISE" ;  break ;
1914                case PL_L_ALS : ls = "ACTIV" ;  break ;
1915                case PL_L_UND : ls = "UNDEF" ;  break ;
1916                case PL_L_ILS4: ls = "ILS 4" ;  break ;
1917                case PL_L_QLS : ls = "QLS" ;    break ;
1918                case PL_L_MLS : ls = "MLS" ;    break ;
1919                case PL_L_HLS : ls = "HLS" ;    break ;
1920                case PL_L_ILS16:ls = "ILS16" ;  break ;
1921#ifdef  lint
1922                default:        ls = "unknown" ; break ;
1923#endif
1924        }
1925        return ls;
1926}
1927
1928char *get_pcmstate(struct s_smc *smc, int np)
1929{
1930        char *pcs ;
1931        
1932        SK_UNUSED(smc) ;
1933
1934        switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) {
1935                case PL_PC0 :   pcs = "OFF" ;           break ;
1936                case PL_PC1 :   pcs = "BREAK" ;         break ;
1937                case PL_PC2 :   pcs = "TRACE" ;         break ;
1938                case PL_PC3 :   pcs = "CONNECT";        break ;
1939                case PL_PC4 :   pcs = "NEXT" ;          break ;
1940                case PL_PC5 :   pcs = "SIGNAL" ;        break ;
1941                case PL_PC6 :   pcs = "JOIN" ;          break ;
1942                case PL_PC7 :   pcs = "VERIFY" ;        break ;
1943                case PL_PC8 :   pcs = "ACTIV" ;         break ;
1944                case PL_PC9 :   pcs = "MAINT" ;         break ;
1945                default :       pcs = "UNKNOWN" ;       break ;
1946        }
1947        return pcs;
1948}
1949
1950void list_phy(struct s_smc *smc)
1951{
1952        struct s_plc *plc ;
1953        int np ;
1954
1955        for (np = 0 ; np < NUMPHYS ; np++) {
1956                plc  = &smc->y[np].plc ;
1957                printf("PHY %d:\tERRORS\t\t\tBREAK_REASONS\t\tSTATES:\n",np) ;
1958                printf("\tsoft_error: %ld \t\tPC_Start : %ld\n",
1959                                                plc->soft_err,plc->b_pcs);
1960                printf("\tparity_err: %ld \t\tTPC exp. : %ld\t\tLine: %s\n",
1961                        plc->parity_err,plc->b_tpc,get_linestate(smc,np)) ;
1962                printf("\tebuf_error: %ld \t\tTNE exp. : %ld\n",
1963                                                plc->ebuf_err,plc->b_tne) ;
1964                printf("\tphyinvalid: %ld \t\tQLS det. : %ld\t\tPCM : %s\n",
1965                        plc->phyinv,plc->b_qls,get_pcmstate(smc,np)) ;
1966                printf("\tviosym_ctr: %ld \t\tILS det. : %ld\n",
1967                                                plc->vsym_ctr,plc->b_ils)  ;
1968                printf("\tmingap_ctr: %ld \t\tHLS det. : %ld\n",
1969                                                plc->mini_ctr,plc->b_hls) ;
1970                printf("\tnodepr_err: %ld\n",plc->np_err) ;
1971                printf("\tTPC_exp : %ld\n",plc->tpc_exp) ;
1972                printf("\tLEM_err : %ld\n",smc->y[np].lem.lem_errors) ;
1973        }
1974}
1975
1976
1977#ifdef  CONCENTRATOR
1978void pcm_lem_dump(struct s_smc *smc)
1979{
1980        int             i ;
1981        struct s_phy    *phy ;
1982        struct fddi_mib_p       *mib ;
1983
1984        char            *entostring() ;
1985
1986        printf("PHY     errors  BER\n") ;
1987        printf("----------------------\n") ;
1988        for (i = 0,phy = smc->y ; i < NUMPHYS ; i++,phy++) {
1989                if (!plc_is_installed(smc,i))
1990                        continue ;
1991                mib = phy->mib ;
1992                printf("%s\t%ld\t10E-%d\n",
1993                        entostring(smc,ENTITY_PHY(i)),
1994                        mib->fddiPORTLem_Ct,
1995                        mib->fddiPORTLer_Estimate) ;
1996        }
1997}
1998#endif
1999#endif
2000