// SPDX-License-Identifier: GPL-2.0-or-later /****************************************************************************** * * (C)Copyright 1998,1999 SysKonnect, * a business unit of Schneider & Koch & Co. Datensysteme GmbH. * * See the file "skfddi.c" for further information. * * The information in this file is provided "AS IS" without warranty. *
******************************************************************************/
/* PCM Physical Connection Management
*/
/* * Hardware independent state machine implemantation * The following external SMT functions are referenced : * * queue_event() * smt_timer_start() * smt_timer_stop() * * The following external HW dependent functions are referenced : * sm_pm_control() * sm_ph_linestate() * * The following HW dependent events are required : * PC_QLS * PC_ILS * PC_HLS * PC_MLS * PC_NSE * PC_LEM *
*/
staticconststruct plt { int timer ; /* relative plc timer address */ int para ; /* default timing parameters */
} pltm[] = {
{ PL_C_MIN, SLOW_C_MIN }, /* min t. to remain Connect State */
{ PL_TL_MIN, SLOW_TL_MIN }, /* min t. to transmit a Line State */
{ PL_TB_MIN, TP_TB_MIN }, /* min break time */
{ PL_T_OUT, TP_T_OUT }, /* Signaling timeout */
{ PL_LC_LENGTH, TP_LC_LENGTH }, /* Link Confidence Test Time */
{ PL_T_SCRUB, TP_T_SCRUB }, /* Scrub Time == MAC TVX time ! */
{ PL_NS_MAX, TP_NS_MAX }, /* max t. that noise is tolerated */
{ 0,0 }
} ;
/* * interrupt mask
*/ #ifdef SUPERNET_3 /* * Do we need the EBUF error during signaling, too, to detect SUPERNET_3 * PLL bug?
*/ staticconstint plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR; #else/* SUPERNET_3 */ /* * We do NOT need the elasticity buffer error during signaling.
*/ staticint plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
PL_PCM_ENABLED | PL_SELF_TEST ; #endif/* SUPERNET_3 */ staticconstint plc_imsk_act = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK |
PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR;
/* internal functions */ staticvoid pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd); staticvoid pc_rcode_actions(struct s_smc *smc, int bit, struct s_phy *phy); staticvoid pc_tcode_actions(struct s_smc *smc, constint bit, struct s_phy *phy); staticvoid reset_lem_struct(struct s_phy *phy); staticvoid plc_init(struct s_smc *smc, int p); staticvoid sm_ph_lem_start(struct s_smc *smc, int np, int threshold); staticvoid sm_ph_lem_stop(struct s_smc *smc, int np); staticvoid sm_ph_linestate(struct s_smc *smc, int phy, int ls); staticvoid real_init_plc(struct s_smc *smc);
/* init PCM state machine (called by driver) clear all PCM vars and flags
*/ void pcm_init(struct s_smc *smc)
{ int i ; int np ; struct s_phy *phy ; struct fddi_mib_p *mib ;
/* * dummy * this is an obsolete public entry point that has to remain * for compat. It is used by various drivers. * the work is now done in real_init_plc() * which is called from pcm_init() ;
*/
}
staticvoid real_init_plc(struct s_smc *smc)
{ int p ;
for (p = 0 ; p < NUMPHYS ; p++)
plc_init(smc,p) ;
}
staticvoid plc_init(struct s_smc *smc, int p)
{ int i ; #ifndef MOT_ELM int rev ; /* Revision of PLC-x */ #endif/* MOT_ELM */
/* transit PCM state machine to MAINT state */
outpw(PLC(p,PL_CNTRL_B),0) ;
outpw(PLC(p,PL_CNTRL_B),PL_PCM_STOP) ;
outpw(PLC(p,PL_CNTRL_A),0) ;
/* * if PLC-S then set control register C
*/ #ifndef MOT_ELM
rev = inpw(PLC(p,PL_STATUS_A)) & PLC_REV_MASK ; if (rev != PLC_REVISION_A) #endif/* MOT_ELM */
{ if (smc->y[p].pmd_scramble) {
outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_S) ; #ifdef MOT_ELM
outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_S) ;
outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_S) ; #endif/* MOT_ELM */
} else {
outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_U) ; #ifdef MOT_ELM
outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_U) ;
outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_U) ; #endif/* MOT_ELM */
}
}
/* * set timer register
*/ for ( i = 0 ; pltm[i].timer; i++) /* set timer parameter reg */
outpw(PLC(p,pltm[i].timer),pltm[i].para) ;
(void)inpw(PLC(p,PL_INTR_EVENT)) ; /* clear interrupt event reg */
plc_clear_irq(smc,p) ;
outpw(PLC(p,PL_INTR_MASK),plc_imsk_na); /* enable non active irq's */
/* * if PCM is configured for class s, it will NOT go to the * REMOVE state if offline (page 3-36;) * in the concentrator, all inactive PHYS always must be in * the remove state * there's no real need to use this feature at all ..
*/ #ifndef CONCENTRATOR if ((smc->s.sas == SMT_SAS) && (p == PS)) {
outpw(PLC(p,PL_CNTRL_B),PL_CLASS_S) ;
} #endif
}
/* * control PCM state machine
*/ staticvoid plc_go_state(struct s_smc *smc, int p, int state)
{
HW_PTR port ; int val ;
SK_UNUSED(smc) ;
port = (HW_PTR) (PLC(p,PL_CNTRL_B)) ;
val = inpw(port) & ~(PL_PCM_CNTRL | PL_MAINT) ;
outpw(port,val) ;
outpw(port,val | state) ;
}
/* * read current line state (called by ECM & PCM)
*/ int sm_pm_get_ls(struct s_smc *smc, int phy)
{ int state ;
#ifdef CONCENTRATOR if (!plc_is_installed(smc,phy)) return PC_QLS; #endif
state = inpw(PLC(phy,PL_STATUS_A)) & PL_LINE_ST ; switch(state) { case PL_L_QLS:
state = PC_QLS ; break ; case PL_L_MLS:
state = PC_MLS ; break ; case PL_L_HLS:
state = PC_HLS ; break ; case PL_L_ILS4: case PL_L_ILS16:
state = PC_ILS ; break ; case PL_L_ALS:
state = PC_LS_PDR ; break ; default :
state = PC_LS_NONE ;
} return state;
}
staticint plc_send_bits(struct s_smc *smc, struct s_phy *phy, int len)
{ int np = phy->np ; /* PHY index */ int n ; int i ;
SK_UNUSED(smc) ;
/* create bit vector */ for (i = len-1,n = 0 ; i >= 0 ; i--) {
n = (n<<1) | phy->t_val[phy->bitn+i] ;
} if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) { #if 0
printf("PL_PCM_SIGNAL is set\n") ; #endif return 1;
} /* write bit[n] & length = 1 to regs */
outpw(PLC(np,PL_VECTOR_LEN),len-1) ; /* len=nr-1 */
outpw(PLC(np,PL_XMIT_VECTOR),n) ; #ifdef DEBUG #if 1 #ifdef DEBUG_BRD if (smc->debug.d_plc & 0x80) #else if (debug.d_plc & 0x80) #endif
printf("SIGNALING bit %d .. %d\n",phy->bitn,phy->bitn+len-1) ; #endif #endif return 0;
}
/* PCM state machine called by dispatcher & fddi_init() (driver) do display state change process event until SM is stable
*/ void pcm(struct s_smc *smc, constint np, int event)
{ int state ; int oldstate ; struct s_phy *phy ; struct fddi_mib_p *mib ;
#ifndef CONCENTRATOR /* * ignore 2nd PHY if SAS
*/ if ((np != PS) && (smc->s.sas == SMT_SAS)) return ; #endif
phy = &smc->y[np] ;
mib = phy->mib ;
oldstate = mib->fddiPORTPCMState ; do {
DB_PCM("PCM %c: state %s%s, event %s",
phy->phy_name,
mib->fddiPORTPCMState & AFLAG ? "ACTIONS " : "",
pcm_states[mib->fddiPORTPCMState & ~AFLAG],
pcm_events[event]);
state = mib->fddiPORTPCMState ;
pcm_fsm(smc,phy,event) ;
event = 0 ;
} while (state != mib->fddiPORTPCMState) ; /* * because the PLC does the bit signaling for us, * we're always in SIGNAL state * the MIB want's to see CONNECT * we therefore fake an entry in the MIB
*/ if (state == PC5_SIGNAL)
mib->fddiPORTPCMStateX = PC3_CONNECT ; else
mib->fddiPORTPCMStateX = state ;
#ifdef FDDI_MIB /* check whether a snmp-trap has to be sent */
if ( mib->fddiPORTPCMState != oldstate ) { /* a real state change took place */
DB_SNMP ("PCM from %d to %d\n", oldstate, mib->fddiPORTPCMState); if ( mib->fddiPORTPCMState == PC0_OFF ) { /* send first trap */
snmp_fddi_trap (smc, 1, (int) mib->fddiPORTIndex );
} elseif ( oldstate == PC0_OFF ) { /* send second trap */
snmp_fddi_trap (smc, 2, (int) mib->fddiPORTIndex );
} elseif ( mib->fddiPORTPCMState != PC2_TRACE &&
oldstate == PC8_ACTIVE ) { /* send third trap */
snmp_fddi_trap (smc, 3, (int) mib->fddiPORTIndex );
} elseif ( mib->fddiPORTPCMState == PC8_ACTIVE ) { /* send fourth trap */
snmp_fddi_trap (smc, 4, (int) mib->fddiPORTIndex );
}
} #endif
pcm_state_change(smc,np,state) ;
}
/* * PCM state machine
*/ staticvoid pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd)
{ int i ; int np = phy->np ; /* PHY index */ struct s_plc *plc ; struct fddi_mib_p *mib ; #ifndef MOT_ELM
u_short plc_rev ; /* Revision of the plc */ #endif/* nMOT_ELM */
plc = &phy->plc ;
mib = phy->mib ;
/* * general transitions independent of state
*/ switch (cmd) { case PC_STOP : /*PC00-PC80*/ if (mib->fddiPORTPCMState != PC9_MAINT) {
GO_STATE(PC0_OFF) ;
AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
FDDI_PORT_EVENT, (u_long) FDDI_PORT_STOP,
smt_get_port_event_word(smc));
} return ; case PC_START : /*PC01-PC81*/ if (mib->fddiPORTPCMState != PC9_MAINT)
GO_STATE(PC1_BREAK) ; return ; case PC_DISABLE : /* PC09-PC99 */
GO_STATE(PC9_MAINT) ;
AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
FDDI_PORT_EVENT, (u_long) FDDI_PORT_DISABLED,
smt_get_port_event_word(smc)); return ; case PC_TIMEOUT_LCT : /* if long or extended LCT */
stop_pcm_timer0(smc,phy) ;
CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ; /* end of LCT is indicate by PCM_CODE (initiate PCM event) */ return ;
}
switch(mib->fddiPORTPCMState) { case ACTIONS(PC0_OFF) :
stop_pcm_timer0(smc,phy) ;
outpw(PLC(np,PL_CNTRL_A),0) ;
CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
sm_ph_lem_stop(smc,np) ; /* disable LEM */
phy->cf_loop = FALSE ;
phy->cf_join = FALSE ;
queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
plc_go_state(smc,np,PL_PCM_STOP) ;
mib->fddiPORTConnectState = PCM_DISABLED ;
ACTIONS_DONE() ; break ; case PC0_OFF: /*PC09*/ if (cmd == PC_MAINT) {
GO_STATE(PC9_MAINT) ; break ;
} break ; case ACTIONS(PC1_BREAK) : /* Stop the LCT timer if we came from Signal state */
stop_pcm_timer0(smc,phy) ;
ACTIONS_DONE() ;
plc_go_state(smc,np,0) ;
CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ;
CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ;
sm_ph_lem_stop(smc,np) ; /* disable LEM */ /* * if vector is already loaded, go to OFF to clear PCM_SIGNAL
*/ #if 0 if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) {
plc_go_state(smc,np,PL_PCM_STOP) ; /* TB_MIN ? */
} #endif /* * Go to OFF state in any case.
*/
plc_go_state(smc,np,PL_PCM_STOP) ;
if (mib->fddiPORTPC_Withhold == PC_WH_NONE)
mib->fddiPORTConnectState = PCM_CONNECTING ;
phy->cf_loop = FALSE ;
phy->cf_join = FALSE ;
queue_event(smc,EVENT_CFM,CF_JOIN+np) ;
phy->ls_flag = FALSE ;
phy->pc_mode = PM_NONE ; /* needed by CFM */
phy->bitn = 0 ; /* bit signaling start bit */ for (i = 0 ; i < 3 ; i++)
pc_tcode_actions(smc,i,phy) ;
/* Set the non-active interrupt mask register */
outpw(PLC(np,PL_INTR_MASK),plc_imsk_na) ;
/* * If the LCT was stopped. There might be a * PCM_CODE interrupt event present. * This must be cleared.
*/
(void)inpw(PLC(np,PL_INTR_EVENT)) ; #ifndef MOT_ELM /* Get the plc revision for revision dependent code */
plc_rev = inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK ;
if (plc_rev != PLC_REV_SN3) #endif/* MOT_ELM */
{ /* * No supernet III PLC, so set Xmit verctor and * length BEFORE starting the state machine.
*/ if (plc_send_bits(smc,phy,3)) { return ;
}
}
/* * Now give the Start command. * - The start command shall be done before setting the bits * to be signaled. (In PLC-S description and PLCS in SN3. * - The start command shall be issued AFTER setting the * XMIT vector and the XMIT length register. * * We do it exactly according this specs for the old PLC and * the new PLCS inside the SN3. * For the usual PLCS we try it the way it is done for the * old PLC and set the XMIT registers again, if the PLC is * not in SIGNAL state. This is done according to an PLCS * errata workaround.
*/
plc_go_state(smc,np,PL_PCM_START) ;
/* * workaround for PLC-S eng. sample errata
*/ #ifdef MOT_ELM if (!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL)) #else/* nMOT_ELM */ if (((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) !=
PLC_REVISION_A) &&
!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL)) #endif/* nMOT_ELM */
{ /* * Set register again (PLCS errata) or the first time * (new SN3 PLCS).
*/
(void) plc_send_bits(smc,phy,3) ;
} /* * end of workaround
*/
errors = lem->lem_errors ; /* * calculation is called on a intervall of 8 seconds * -> this means, that one error in 8 sec. is one of 8*125*10E6 * the same as BER = 10E-9 * Please note: * -> 9 errors in 8 seconds mean: * BER = 9 * 10E-9 and this is * < 10E-8, so the limit of 10E-8 is not reached!
*/
if (!errors) ber = 15 ; elseif (errors <= 9) ber = 9 ; elseif (errors <= 99) ber = 8 ; elseif (errors <= 999) ber = 7 ; elseif (errors <= 9999) ber = 6 ; elseif (errors <= 99999) ber = 5 ; elseif (errors <= 999999) ber = 4 ; elseif (errors <= 9999999) ber = 3 ; elseif (errors <= 99999999) ber = 2 ; elseif (errors <= 999999999) ber = 1 ; else ber = 0 ;
/* * weighted average
*/
ber *= 100 ;
lem->lem_float_ber = lem->lem_float_ber * 7 + ber * 3 ;
lem->lem_float_ber /= 10 ;
mib->fddiPORTLer_Estimate = lem->lem_float_ber / 100 ; if (mib->fddiPORTLer_Estimate < 4) {
mib->fddiPORTLer_Estimate = 4 ;
}
if (lem->lem_errors) {
DB_PCMN(1, "LEM %c :", phy->np == PB ? 'B' : 'A');
DB_PCMN(1, "errors : %ld", lem->lem_errors);
DB_PCMN(1, "sum_errors : %ld", mib->fddiPORTLem_Ct);
DB_PCMN(1, "current BER : 10E-%d", ber / 100);
DB_PCMN(1, "float BER : 10E-(%d/100)", lem->lem_float_ber);
DB_PCMN(1, "avg. BER : 10E-%d", mib->fddiPORTLer_Estimate);
}
if ( mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Cutoff) {
phy->pc_lem_fail = TRUE ; /* flag */
mib->fddiPORTLem_Reject_Ct++ ; /* * "forgive 10e-2" if we cutoff so we can come * up again ..
*/
lem->lem_float_ber += 2*100 ;
/* * PCM pseudo code * receive actions are called AFTER the bit n is received, * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received
*/
switch(bit) { case 0:
phy->t_val[0] = 0 ; /* no escape used */ break ; case 1: if (mib->fddiPORTMy_Type == TS || mib->fddiPORTMy_Type == TM)
phy->t_val[1] = 1 ; else
phy->t_val[1] = 0 ; break ; case 2 : if (mib->fddiPORTMy_Type == TB || mib->fddiPORTMy_Type == TM)
phy->t_val[2] = 1 ; else
phy->t_val[2] = 0 ; break ; case 3:
{ int type,ne ; int policy ;
type = mib->fddiPORTMy_Type ;
ne = mib->fddiPORTNeighborType ;
policy = smc->mib.fddiSMTConnectionPolicy ;
phy->t_val[3] = 1 ; /* Accept connection */ switch (type) { case TA : if (
((policy & POLICY_AA) && ne == TA) ||
((policy & POLICY_AB) && ne == TB) ||
((policy & POLICY_AS) && ne == TS) ||
((policy & POLICY_AM) && ne == TM) )
phy->t_val[3] = 0 ; /* Reject */ break ; case TB : if (
((policy & POLICY_BA) && ne == TA) ||
((policy & POLICY_BB) && ne == TB) ||
((policy & POLICY_BS) && ne == TS) ||
((policy & POLICY_BM) && ne == TM) )
phy->t_val[3] = 0 ; /* Reject */ break ; case TS : if (
((policy & POLICY_SA) && ne == TA) ||
((policy & POLICY_SB) && ne == TB) ||
((policy & POLICY_SS) && ne == TS) ||
((policy & POLICY_SM) && ne == TM) )
phy->t_val[3] = 0 ; /* Reject */ break ; case TM : if ( ne == TM ||
((policy & POLICY_MA) && ne == TA) ||
((policy & POLICY_MB) && ne == TB) ||
((policy & POLICY_MS) && ne == TS) ||
((policy & POLICY_MM) && ne == TM) )
phy->t_val[3] = 0 ; /* Reject */ break ;
} #ifndef SLIM_SMT /* * detect undesirable connection attempt event
*/ if ( (type == TA && ne == TA ) ||
(type == TA && ne == TS ) ||
(type == TB && ne == TB ) ||
(type == TB && ne == TS ) ||
(type == TS && ne == TA ) ||
(type == TS && ne == TB ) ) {
smt_srf_event(smc,SMT_EVENT_PORT_CONNECTION,
(int) (INDEX_PORT+ phy->np) ,0) ;
} #endif
} break ; case 4: if (mib->fddiPORTPC_Withhold == PC_WH_NONE) { if (phy->pc_lem_fail) {
phy->t_val[4] = 1 ; /* long */
phy->t_val[5] = 0 ;
} else {
phy->t_val[4] = 0 ; if (mib->fddiPORTLCTFail_Ct > 0)
phy->t_val[5] = 1 ; /* medium */ else
phy->t_val[5] = 0 ; /* short */
/* * Implementers choice: use medium * instead of short when undesired * connection attempt is made.
*/ if (phy->wc_flag)
phy->t_val[5] = 1 ; /* medium */
}
mib->fddiPORTConnectState = PCM_CONNECTING ;
} else {
mib->fddiPORTConnectState = PCM_STANDBY ;
phy->t_val[4] = 1 ; /* extended */
phy->t_val[5] = 1 ;
} break ; case 5: break ; case 6: /* we do NOT have a MAC for LCT */
phy->t_val[6] = 0 ; break ; case 7:
phy->cf_loop = FALSE ;
lem_check_lct(smc,phy) ; if (phy->pc_lem_fail) {
DB_PCMN(1, "PCM %c : E104 LCT failed", phy->phy_name);
phy->t_val[7] = 1 ;
} else
phy->t_val[7] = 0 ; break ; case 8:
phy->t_val[8] = 0 ; /* Don't request MAC loopback */ break ; case 9:
phy->cf_loop = 0 ; if ((mib->fddiPORTPC_Withhold != PC_WH_NONE) ||
((smc->s.sas == SMT_DAS) && (phy->wc_flag))) {
queue_event(smc,EVENT_PCM+np,PC_START) ; break ;
}
phy->t_val[9] = FALSE ; switch (smc->s.sas) { case SMT_DAS : /* * MAC intended on output
*/ if (phy->pc_mode == PM_TREE) { if ((np == PB) || ((np == PA) &&
(smc->y[PB].mib->fddiPORTConnectState !=
PCM_ACTIVE)))
phy->t_val[9] = TRUE ;
} else { if (np == PB)
phy->t_val[9] = TRUE ;
} break ; case SMT_SAS : if (np == PS)
phy->t_val[9] = TRUE ; break ; #ifdef CONCENTRATOR case SMT_NAC : /* * MAC intended on output
*/ if (np == PB)
phy->t_val[9] = TRUE ; break ; #endif
}
mib->fddiPORTMacIndicated.T_val = phy->t_val[9] ; break ;
}
DB_PCMN(1, "SIG snd %x %x:", bit, phy->t_val[bit]);
}
/* * return status twisted (called by SMT)
*/ int pcm_status_twisted(struct s_smc *smc)
{ int twist = 0 ; if (smc->s.sas != SMT_DAS) return 0; if (smc->y[PA].twisted && (smc->y[PA].mib->fddiPORTPCMState == PC8_ACTIVE))
twist |= 1 ; if (smc->y[PB].twisted && (smc->y[PB].mib->fddiPORTPCMState == PC8_ACTIVE))
twist |= 2 ; return twist;
}
/* * return status (called by SMT) * type * state * remote phy type * remote mac yes/no
*/ void pcm_status_state(struct s_smc *smc, int np, int *type, int *state, int *remote, int *mac)
{ struct s_phy *phy = &smc->y[np] ; struct fddi_mib_p *mib ;
mib = phy->mib ;
/* remote PHY type and MAC - set only if active */
*mac = 0 ;
*type = mib->fddiPORTMy_Type ; /* our PHY type */
*state = mib->fddiPORTConnectState ;
*remote = mib->fddiPORTNeighborType ;
/* * return rooted station status (called by SMT)
*/ int pcm_rooted_station(struct s_smc *smc)
{ int n ;
for (n = 0 ; n < NUMPHYS ; n++) { if (smc->y[n].mib->fddiPORTPCMState == PC8_ACTIVE &&
smc->y[n].mib->fddiPORTNeighborType == TM) return 0;
} return 1;
}
/* * Interrupt actions for PLC & PCM events
*/ void plc_irq(struct s_smc *smc, int np, unsignedint cmd) /* int np; PHY index */
{ struct s_phy *phy = &smc->y[np] ; struct s_plc *plc = &phy->plc ; int n ; #ifdef SUPERNET_3 int corr_mask ; #endif/* SUPERNET_3 */ int i ;
if (np >= smc->s.numphys) {
plc->soft_err++ ; return ;
} if (cmd & PL_EBUF_ERR) { /* elastic buff. det. over-|underflow*/ /* * Check whether the SRF Condition occurred.
*/ if (!plc->ebuf_cont && phy->mib->fddiPORTPCMState == PC8_ACTIVE){ /* * This is the real Elasticity Error. * More than one in a row are treated as a * single one. * Only count this in the active state.
*/
phy->mib->fddiPORTEBError_Ct ++ ;
}
plc->ebuf_err++ ; if (plc->ebuf_cont <= 1000) { /* * Prevent counter from being wrapped after * hanging years in that interrupt.
*/
plc->ebuf_cont++ ; /* Ebuf continuous error */
}
#ifdef SUPERNET_3 if (plc->ebuf_cont == 1000 &&
((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) ==
PLC_REV_SN3)) { /* * This interrupt remeained high for at least * 1000 consecutive interrupt calls. * * This is caused by a hardware error of the * ORION part of the Supernet III chipset. * * Disable this bit from the mask.
*/
corr_mask = (plc_imsk_na & ~PL_EBUF_ERR) ;
outpw(PLC(np,PL_INTR_MASK),corr_mask);
/* * Disconnect from the ring. * Call the driver with the reset indication.
*/
queue_event(smc,EVENT_ECM,EC_DISCONNECT) ;
/* * Make an error log entry.
*/
SMT_ERR_LOG(smc,SMT_E0136, SMT_E0136_MSG) ;
/* * Indicate the Reset.
*/
drv_reset_indication(smc) ;
} #endif/* SUPERNET_3 */
} else { /* Reset the continuous error variable */
plc->ebuf_cont = 0 ; /* reset Ebuf continuous error */
} if (cmd & PL_PHYINV) { /* physical layer invalid signal */
plc->phyinv++ ;
} if (cmd & PL_VSYM_CTR) { /* violation symbol counter has incr.*/
plc->vsym_ctr++ ;
} if (cmd & PL_MINI_CTR) { /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/
plc->mini_ctr++ ;
} if (cmd & PL_LE_CTR) { /* link error event counter */ int j ;
/* * note: PL_LINK_ERR_CTR MUST be read to clear it
*/
j = inpw(PLC(np,PL_LE_THRESHOLD)) ;
i = inpw(PLC(np,PL_LINK_ERR_CTR)) ;
if (i < j) { /* wrapped around */
i += 256 ;
}
if (phy->lem.lem_on) { /* Note: Lem errors shall only be counted when * link is ACTIVE or LCT is active.
*/
phy->lem.lem_errors += i ;
phy->mib->fddiPORTLem_Ct += i ;
}
} if (cmd & PL_TPC_EXPIRED) { /* TPC timer reached zero */ if (plc->p_state == PS_LCT) { /* * end of LCT
*/
;
}
plc->tpc_exp++ ;
} if (cmd & PL_LS_MATCH) { /* LS == LS in PLC_CNTRL_B's MATCH_LS*/ switch (inpw(PLC(np,PL_CNTRL_B)) & PL_MATCH_LS) { case PL_I_IDLE : phy->curr_ls = PC_ILS ; break ; case PL_I_HALT : phy->curr_ls = PC_HLS ; break ; case PL_I_MASTR : phy->curr_ls = PC_MLS ; break ; case PL_I_QUIET : phy->curr_ls = PC_QLS ; break ;
}
} if (cmd & PL_PCM_BREAK) { /* PCM has entered the BREAK state */ int reason;
switch (reason) { case PL_B_PCS : plc->b_pcs++ ; break ; case PL_B_TPC : plc->b_tpc++ ; break ; case PL_B_TNE : plc->b_tne++ ; break ; case PL_B_QLS : plc->b_qls++ ; break ; case PL_B_ILS : plc->b_ils++ ; break ; case PL_B_HLS : plc->b_hls++ ; break ;
}
/*jd 05-Aug-1999 changed: Bug #10419 */
DB_PCMN(1, "PLC %d: MDcF = %x", np, smc->e.DisconnectFlag); if (smc->e.DisconnectFlag == FALSE) {
DB_PCMN(1, "PLC %d: restart (reason %x)", np, reason);
queue_event(smc,EVENT_PCM+np,PC_START) ;
} else {
DB_PCMN(1, "PLC %d: NO!! restart (reason %x)",
np, reason);
} return ;
} /* * If both CODE & ENABLE are set ignore enable
*/ if (cmd & PL_PCM_CODE) { /* receive last sign.-bit | LCT complete */
queue_event(smc,EVENT_PCM+np,PC_SIGNAL) ;
n = inpw(PLC(np,PL_RCV_VECTOR)) ; for (i = 0 ; i < plc->p_bits ; i++) {
phy->r_val[plc->p_start+i] = n & 1 ;
n >>= 1 ;
}
} elseif (cmd & PL_PCM_ENABLED) { /* asserted SC_JOIN, scrub.completed*/
queue_event(smc,EVENT_PCM+np,PC_JOIN) ;
} if (cmd & PL_TRACE_PROP) { /* MLS while PC8_ACTIV || PC2_TRACE */ /*PC22b*/ if (!phy->tr_flag) {
DB_PCMN(1, "PCM : irq TRACE_PROP %d %d",
np, smc->mib.fddiSMTECMState);
phy->tr_flag = TRUE ;
smc->e.trace_prop |= ENTITY_BIT(ENTITY_PHY(np)) ;
queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ;
}
} /* * filter PLC glitch ??? * QLS || HLS only while in PC2_TRACE state
*/ if ((cmd & PL_SELF_TEST) && (phy->mib->fddiPORTPCMState == PC2_TRACE)) { /*PC22a*/ if (smc->e.path_test == PT_PASSED) {
DB_PCMN(1, "PCM : state = %s %d",
get_pcmstate(smc, np),
phy->mib->fddiPORTPCMState);
smc->e.path_test = PT_PENDING ;
queue_event(smc,EVENT_ECM,EC_PATH_TEST) ;
}
} if (cmd & PL_TNE_EXPIRED) { /* TNE: length of noise events */ /* break_required (TNE > NS_Max) */ if (phy->mib->fddiPORTPCMState == PC8_ACTIVE) { if (!phy->tr_flag) {
DB_PCMN(1, "PCM %c : PC81 %s",
phy->phy_name, "NSE");
queue_event(smc, EVENT_PCM + np, PC_START); return;
}
}
} #if 0 if (cmd & PL_NP_ERR) { /* NP has requested to r/w an inv reg*/ /* * It's a bug by AMD
*/
plc->np_err++ ;
} /* pin inactiv (GND) */ if (cmd & PL_PARITY_ERR) { /* p. error dedected on TX9-0 inp */
plc->parity_err++ ;
} if (cmd & PL_LSDO) { /* carrier detected */
;
} #endif
}
#ifdef DEBUG /* * fill state struct
*/ void pcm_get_state(struct s_smc *smc, struct smt_state *state)
{ struct s_phy *phy ; struct pcm_state *pcs ; int i ; int ii ; short rbits ; short tbits ; struct fddi_mib_p *mib ;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.