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