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