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