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 } 535