525 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/******************************************************************************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 ECM15 Entity Coordination Management16 Hardware independent state machine17*/18 19/*20 * Hardware independent state machine implemantation21 * 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 * NONE33 *34 */35 36#include "h/types.h"37#include "h/fddi.h"38#include "h/smc.h"39 40#define KERNEL41#include "h/smtstate.h"42 43/*44 * FSM Macros45 */46#define AFLAG 0x1047#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 names62 */63static 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 names70 */71static 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.h79 * struct s_ecm80 */81 82/*83 * function declarations84 */85 86static void ecm_fsm(struct s_smc *smc, int cmd);87static void start_ecm_timer(struct s_smc *smc, u_long value, int event);88static void stop_ecm_timer(struct s_smc *smc);89static void prop_actions(struct s_smc *smc);90 91/*92 init ECM state machine93 clear all ECM vars and flags94*/95void 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 machine106 called by dispatcher107 108 do109 display state change110 process event111 until SM is stable112*/113void 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 event131*/132static 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 test164 */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.fddiSMTBypassPresent173 && 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 timeout272 */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 poll384 */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 CONCENTRATOR412/*413 * trace propagation actions for SAS & DAS414 */415static 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#else462/*463 * trace propagation actions for Concentrator464 */465static 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#endif505 506 507/*508 * SMT timer interface509 * start ECM timer510 */511static 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 interface518 * stop ECM timer519 */520static 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