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