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