xref: /kernel/linux/linux-5.10/drivers/net/fddi/skfp/ecm.c (revision 8c2ecf20)
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	SMT ECM
15	Entity Coordination Management
16	Hardware independent state machine
17*/
18
19/*
20 * Hardware independent state machine implemantation
21 * The following external SMT functions are referenced :
22 *
23 * 		queue_event()
24 * 		smt_timer_start()
25 * 		smt_timer_stop()
26 *
27 * 	The following external HW dependent functions are referenced :
28 * 		sm_pm_bypass_req()
29 * 		sm_pm_get_ls()
30 *
31 * 	The following HW dependent events are required :
32 *		NONE
33 *
34 */
35
36#include "h/types.h"
37#include "h/fddi.h"
38#include "h/smc.h"
39
40#define KERNEL
41#include "h/smtstate.h"
42
43#ifndef	lint
44static const char ID_sccs[] = "@(#)ecm.c	2.7 99/08/05 (C) SK " ;
45#endif
46
47/*
48 * FSM Macros
49 */
50#define AFLAG	0x10
51#define GO_STATE(x)	(smc->mib.fddiSMTECMState = (x)|AFLAG)
52#define ACTIONS_DONE()	(smc->mib.fddiSMTECMState &= ~AFLAG)
53#define ACTIONS(x)	(x|AFLAG)
54
55#define EC0_OUT		0			/* not inserted */
56#define EC1_IN		1			/* inserted */
57#define EC2_TRACE	2			/* tracing */
58#define EC3_LEAVE	3			/* leaving the ring */
59#define EC4_PATH_TEST	4			/* performing path test */
60#define EC5_INSERT	5			/* bypass being turned on */
61#define EC6_CHECK	6			/* checking bypass */
62#define EC7_DEINSERT	7			/* bypass being turnde off */
63
64/*
65 * symbolic state names
66 */
67static const char * const ecm_states[] = {
68	"EC0_OUT","EC1_IN","EC2_TRACE","EC3_LEAVE","EC4_PATH_TEST",
69	"EC5_INSERT","EC6_CHECK","EC7_DEINSERT"
70} ;
71
72/*
73 * symbolic event names
74 */
75static const char * const ecm_events[] = {
76	"NONE","EC_CONNECT","EC_DISCONNECT","EC_TRACE_PROP","EC_PATH_TEST",
77	"EC_TIMEOUT_TD","EC_TIMEOUT_TMAX",
78	"EC_TIMEOUT_IMAX","EC_TIMEOUT_INMAX","EC_TEST_DONE"
79} ;
80
81/*
82 * all Globals  are defined in smc.h
83 * struct s_ecm
84 */
85
86/*
87 * function declarations
88 */
89
90static void ecm_fsm(struct s_smc *smc, int cmd);
91static void start_ecm_timer(struct s_smc *smc, u_long value, int event);
92static void stop_ecm_timer(struct s_smc *smc);
93static void prop_actions(struct s_smc *smc);
94
95/*
96	init ECM state machine
97	clear all ECM vars and flags
98*/
99void ecm_init(struct s_smc *smc)
100{
101	smc->e.path_test = PT_PASSED ;
102	smc->e.trace_prop = 0 ;
103	smc->e.sb_flag = 0 ;
104	smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ;
105	smc->e.ecm_line_state = FALSE ;
106}
107
108/*
109	ECM state machine
110	called by dispatcher
111
112	do
113		display state change
114		process event
115	until SM is stable
116*/
117void ecm(struct s_smc *smc, int event)
118{
119	int	state ;
120
121	do {
122		DB_ECM("ECM : state %s%s event %s",
123		       smc->mib.fddiSMTECMState & AFLAG ? "ACTIONS " : "",
124		       ecm_states[smc->mib.fddiSMTECMState & ~AFLAG],
125		       ecm_events[event]);
126		state = smc->mib.fddiSMTECMState ;
127		ecm_fsm(smc,event) ;
128		event = 0 ;
129	} while (state != smc->mib.fddiSMTECMState) ;
130	ecm_state_change(smc,(int)smc->mib.fddiSMTECMState) ;
131}
132
133/*
134	process ECM event
135*/
136static void ecm_fsm(struct s_smc *smc, int cmd)
137{
138	int ls_a ;			/* current line state PHY A */
139	int ls_b ;			/* current line state PHY B */
140	int	p ;			/* ports */
141
142
143	smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ;
144	if (cmd == EC_CONNECT)
145		smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
146
147	/* For AIX event notification: */
148	/* Is a disconnect  command remotely issued ? */
149	if (cmd == EC_DISCONNECT &&
150		smc->mib.fddiSMTRemoteDisconnectFlag == TRUE)
151		AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long)
152			FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc),
153			smt_get_error_word(smc) );
154
155	/*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/
156	if (cmd == EC_CONNECT) {
157		smc->e.DisconnectFlag = FALSE ;
158	}
159	else if (cmd == EC_DISCONNECT) {
160		smc->e.DisconnectFlag = TRUE ;
161	}
162
163	switch(smc->mib.fddiSMTECMState) {
164	case ACTIONS(EC0_OUT) :
165		/*
166		 * We do not perform a path test
167		 */
168		smc->e.path_test = PT_PASSED ;
169		smc->e.ecm_line_state = FALSE ;
170		stop_ecm_timer(smc) ;
171		ACTIONS_DONE() ;
172		break ;
173	case EC0_OUT:
174		/*EC01*/
175		if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent
176			&& smc->e.path_test==PT_PASSED) {
177			GO_STATE(EC1_IN) ;
178			break ;
179		}
180		/*EC05*/
181		else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) &&
182			smc->mib.fddiSMTBypassPresent &&
183			(smc->s.sas == SMT_DAS)) {
184			GO_STATE(EC5_INSERT) ;
185			break ;
186		}
187		break;
188	case ACTIONS(EC1_IN) :
189		stop_ecm_timer(smc) ;
190		smc->e.trace_prop = 0 ;
191		sm_ma_control(smc,MA_TREQ) ;
192		for (p = 0 ; p < NUMPHYS ; p++)
193			if (smc->mib.p[p].fddiPORTHardwarePresent)
194				queue_event(smc,EVENT_PCMA+p,PC_START) ;
195		ACTIONS_DONE() ;
196		break ;
197	case EC1_IN:
198		/*EC12*/
199		if (cmd == EC_TRACE_PROP) {
200			prop_actions(smc) ;
201			GO_STATE(EC2_TRACE) ;
202			break ;
203		}
204		/*EC13*/
205		else if (cmd == EC_DISCONNECT) {
206			GO_STATE(EC3_LEAVE) ;
207			break ;
208		}
209		break;
210	case ACTIONS(EC2_TRACE) :
211		start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration),
212			EC_TIMEOUT_TMAX) ;
213		ACTIONS_DONE() ;
214		break ;
215	case EC2_TRACE :
216		/*EC22*/
217		if (cmd == EC_TRACE_PROP) {
218			prop_actions(smc) ;
219			GO_STATE(EC2_TRACE) ;
220			break ;
221		}
222		/*EC23a*/
223		else if (cmd == EC_DISCONNECT) {
224			smc->e.path_test = PT_EXITING ;
225			GO_STATE(EC3_LEAVE) ;
226			break ;
227		}
228		/*EC23b*/
229		else if (smc->e.path_test == PT_PENDING) {
230			GO_STATE(EC3_LEAVE) ;
231			break ;
232		}
233		/*EC23c*/
234		else if (cmd == EC_TIMEOUT_TMAX) {
235			/* Trace_Max is expired */
236			/* -> send AIX_EVENT */
237			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS,
238				(u_long) FDDI_SMT_ERROR, (u_long)
239				FDDI_TRACE_MAX, smt_get_error_word(smc));
240			smc->e.path_test = PT_PENDING ;
241			GO_STATE(EC3_LEAVE) ;
242			break ;
243		}
244		break ;
245	case ACTIONS(EC3_LEAVE) :
246		start_ecm_timer(smc,smc->s.ecm_td_min,EC_TIMEOUT_TD) ;
247		for (p = 0 ; p < NUMPHYS ; p++)
248			queue_event(smc,EVENT_PCMA+p,PC_STOP) ;
249		ACTIONS_DONE() ;
250		break ;
251	case EC3_LEAVE:
252		/*EC30*/
253		if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent &&
254			(smc->e.path_test != PT_PENDING)) {
255			GO_STATE(EC0_OUT) ;
256			break ;
257		}
258		/*EC34*/
259		else if (cmd == EC_TIMEOUT_TD &&
260			(smc->e.path_test == PT_PENDING)) {
261			GO_STATE(EC4_PATH_TEST) ;
262			break ;
263		}
264		/*EC31*/
265		else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
266			GO_STATE(EC1_IN) ;
267			break ;
268		}
269		/*EC33*/
270		else if (cmd == EC_DISCONNECT &&
271			smc->e.path_test == PT_PENDING) {
272			smc->e.path_test = PT_EXITING ;
273			/*
274			 * stay in state - state will be left via timeout
275			 */
276		}
277		/*EC37*/
278		else if (cmd == EC_TIMEOUT_TD &&
279			smc->mib.fddiSMTBypassPresent &&
280			smc->e.path_test != PT_PENDING) {
281			GO_STATE(EC7_DEINSERT) ;
282			break ;
283		}
284		break ;
285	case ACTIONS(EC4_PATH_TEST) :
286		stop_ecm_timer(smc) ;
287		smc->e.path_test = PT_TESTING ;
288		start_ecm_timer(smc,smc->s.ecm_test_done,EC_TEST_DONE) ;
289		/* now perform path test ... just a simulation */
290		ACTIONS_DONE() ;
291		break ;
292	case EC4_PATH_TEST :
293		/* path test done delay */
294		if (cmd == EC_TEST_DONE)
295			smc->e.path_test = PT_PASSED ;
296
297		if (smc->e.path_test == PT_FAILED)
298			RS_SET(smc,RS_PATHTEST) ;
299
300		/*EC40a*/
301		if (smc->e.path_test == PT_FAILED &&
302			!smc->mib.fddiSMTBypassPresent) {
303			GO_STATE(EC0_OUT) ;
304			break ;
305		}
306		/*EC40b*/
307		else if (cmd == EC_DISCONNECT &&
308			!smc->mib.fddiSMTBypassPresent) {
309			GO_STATE(EC0_OUT) ;
310			break ;
311		}
312		/*EC41*/
313		else if (smc->e.path_test == PT_PASSED) {
314			GO_STATE(EC1_IN) ;
315			break ;
316		}
317		/*EC47a*/
318		else if (smc->e.path_test == PT_FAILED &&
319			smc->mib.fddiSMTBypassPresent) {
320			GO_STATE(EC7_DEINSERT) ;
321			break ;
322		}
323		/*EC47b*/
324		else if (cmd == EC_DISCONNECT &&
325			smc->mib.fddiSMTBypassPresent) {
326			GO_STATE(EC7_DEINSERT) ;
327			break ;
328		}
329		break ;
330	case ACTIONS(EC5_INSERT) :
331		sm_pm_bypass_req(smc,BP_INSERT);
332		start_ecm_timer(smc,smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ;
333		ACTIONS_DONE() ;
334		break ;
335	case EC5_INSERT :
336		/*EC56*/
337		if (cmd == EC_TIMEOUT_INMAX) {
338			GO_STATE(EC6_CHECK) ;
339			break ;
340		}
341		/*EC57*/
342		else if (cmd == EC_DISCONNECT) {
343			GO_STATE(EC7_DEINSERT) ;
344			break ;
345		}
346		break ;
347	case ACTIONS(EC6_CHECK) :
348		/*
349		 * in EC6_CHECK, we *POLL* the line state !
350		 * check whether both bypass switches have switched.
351		 */
352		start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
353		smc->e.ecm_line_state = TRUE ;	/* flag to pcm: report Q/HLS */
354		ACTIONS_DONE() ;
355		break ;
356	case EC6_CHECK :
357		ls_a = sm_pm_get_ls(smc,PA) ;
358		ls_b = sm_pm_get_ls(smc,PB) ;
359
360		/*EC61*/
361		if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) &&
362		    ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) {
363			smc->e.sb_flag = FALSE ;
364			smc->e.ecm_line_state = FALSE ;
365			GO_STATE(EC1_IN) ;
366			break ;
367		}
368		/*EC66*/
369		else if (!smc->e.sb_flag &&
370			 (((ls_a == PC_ILS) && (ls_b == PC_QLS)) ||
371			  ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){
372			smc->e.sb_flag = TRUE ;
373			DB_ECMN(1, "ECM : EC6_CHECK - stuck bypass");
374			AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
375				FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK,
376				smt_get_error_word(smc));
377		}
378		/*EC67*/
379		else if (cmd == EC_DISCONNECT) {
380			smc->e.ecm_line_state = FALSE ;
381			GO_STATE(EC7_DEINSERT) ;
382			break ;
383		}
384		else {
385			/*
386			 * restart poll
387			 */
388			start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
389		}
390		break ;
391	case ACTIONS(EC7_DEINSERT) :
392		sm_pm_bypass_req(smc,BP_DEINSERT);
393		start_ecm_timer(smc,smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ;
394		ACTIONS_DONE() ;
395		break ;
396	case EC7_DEINSERT:
397		/*EC70*/
398		if (cmd == EC_TIMEOUT_IMAX) {
399			GO_STATE(EC0_OUT) ;
400			break ;
401		}
402		/*EC75*/
403		else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
404			GO_STATE(EC5_INSERT) ;
405			break ;
406		}
407		break;
408	default:
409		SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ;
410		break;
411	}
412}
413
414#ifndef	CONCENTRATOR
415/*
416 * trace propagation actions for SAS & DAS
417 */
418static void prop_actions(struct s_smc *smc)
419{
420	int	port_in = 0 ;
421	int	port_out = 0 ;
422
423	RS_SET(smc,RS_EVENT) ;
424	switch (smc->s.sas) {
425	case SMT_SAS :
426		port_in = port_out = pcm_get_s_port(smc) ;
427		break ;
428	case SMT_DAS :
429		port_in = cfm_get_mac_input(smc) ;	/* PA or PB */
430		port_out = cfm_get_mac_output(smc) ;	/* PA or PB */
431		break ;
432	case SMT_NAC :
433		SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ;
434		return ;
435	}
436
437	DB_ECM("ECM : prop_actions - trace_prop %lu", smc->e.trace_prop);
438	DB_ECM("ECM : prop_actions - in %d out %d", port_in, port_out);
439
440	if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
441		/* trace initiatior */
442		DB_ECM("ECM : initiate TRACE on PHY %c", 'A' + port_in - PA);
443		queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ;
444	}
445	else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) &&
446		port_out != PA) {
447		/* trace propagate upstream */
448		DB_ECM("ECM : propagate TRACE on PHY B");
449		queue_event(smc,EVENT_PCMB,PC_TRACE) ;
450	}
451	else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) &&
452		port_out != PB) {
453		/* trace propagate upstream */
454		DB_ECM("ECM : propagate TRACE on PHY A");
455		queue_event(smc,EVENT_PCMA,PC_TRACE) ;
456	}
457	else {
458		/* signal trace termination */
459		DB_ECM("ECM : TRACE terminated");
460		smc->e.path_test = PT_PENDING ;
461	}
462	smc->e.trace_prop = 0 ;
463}
464#else
465/*
466 * trace propagation actions for Concentrator
467 */
468static void prop_actions(struct s_smc *smc)
469{
470	int	initiator ;
471	int	upstream ;
472	int	p ;
473
474	RS_SET(smc,RS_EVENT) ;
475	while (smc->e.trace_prop) {
476		DB_ECM("ECM : prop_actions - trace_prop %d",
477		       smc->e.trace_prop);
478
479		if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
480			initiator = ENTITY_MAC ;
481			smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ;
482			DB_ECM("ECM: MAC initiates trace");
483		}
484		else {
485			for (p = NUMPHYS-1 ; p >= 0 ; p--) {
486				if (smc->e.trace_prop &
487					ENTITY_BIT(ENTITY_PHY(p)))
488					break ;
489			}
490			initiator = ENTITY_PHY(p) ;
491			smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ;
492		}
493		upstream = cem_get_upstream(smc,initiator) ;
494
495		if (upstream == ENTITY_MAC) {
496			/* signal trace termination */
497			DB_ECM("ECM : TRACE terminated");
498			smc->e.path_test = PT_PENDING ;
499		}
500		else {
501			/* trace propagate upstream */
502			DB_ECM("ECM : propagate TRACE on PHY %d", upstream);
503			queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ;
504		}
505	}
506}
507#endif
508
509
510/*
511 * SMT timer interface
512 *	start ECM timer
513 */
514static void start_ecm_timer(struct s_smc *smc, u_long value, int event)
515{
516	smt_timer_start(smc,&smc->e.ecm_timer,value,EV_TOKEN(EVENT_ECM,event));
517}
518
519/*
520 * SMT timer interface
521 *	stop ECM timer
522 */
523static void stop_ecm_timer(struct s_smc *smc)
524{
525	if (smc->e.ecm_timer.tm_active)
526		smt_timer_stop(smc,&smc->e.ecm_timer) ;
527}
528