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