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