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)) { 191 GO_STATE(EC5_INSERT) ; 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) 201 queue_event(smc,EVENT_PCMA+p,PC_START) ; 202 ACTIONS_DONE() ; 203 break ; 204 case EC1_IN: 205 /*EC12*/ 206 if (cmd == EC_TRACE_PROP) { 207 prop_actions(smc) ; 208 GO_STATE(EC2_TRACE) ; 209 break ; 210 } 211 /*EC13*/ 212 else if (cmd == EC_DISCONNECT) { 213 GO_STATE(EC3_LEAVE) ; 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) ; 226 GO_STATE(EC2_TRACE) ; 227 break ; 228 } 229 /*EC23a*/ 230 else if (cmd == EC_DISCONNECT) { 231 smc->e.path_test = PT_EXITING ; 232 GO_STATE(EC3_LEAVE) ; 233 break ; 234 } 235 /*EC23b*/ 236 else if (smc->e.path_test == PT_PENDING) { 237 GO_STATE(EC3_LEAVE) ; 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 ; 248 GO_STATE(EC3_LEAVE) ; 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++) 255 queue_event(smc,EVENT_PCMA+p,PC_STOP) ; 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)) { 268 GO_STATE(EC4_PATH_TEST) ; 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) { 288 GO_STATE(EC7_DEINSERT) ; 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) { 327 GO_STATE(EC7_DEINSERT) ; 328 break ; 329 } 330 /*EC47b*/ 331 else if (cmd == EC_DISCONNECT && 332 smc->mib.fddiSMTBypassPresent) { 333 GO_STATE(EC7_DEINSERT) ; 334 break ; 335 } 336 break ; 337 case ACTIONS(EC5_INSERT) : 338 sm_pm_bypass_req(smc,BP_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) { 345 GO_STATE(EC6_CHECK) ; 346 break ; 347 } 348 /*EC57*/ 349 else if (cmd == EC_DISCONNECT) { 350 GO_STATE(EC7_DEINSERT) ; 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 ; 390 GO_STATE(EC7_DEINSERT) ; 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) : 401 sm_pm_bypass_req(smc,BP_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) { 413 GO_STATE(EC5_INSERT) ; 414 break ; 415 } 416 break; 417 default: 418 SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ; 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 : 442 SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ; 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) ; 458 queue_event(smc,EVENT_PCMB,PC_TRACE) ; 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) ; 464 queue_event(smc,EVENT_PCMA,PC_TRACE) ; 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 & 496 ENTITY_BIT(ENTITY_PHY(p))) 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 } 537