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