Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /******************************************************************************
0003  *
0004  *  (C)Copyright 1998,1999 SysKonnect,
0005  *  a business unit of Schneider & Koch & Co. Datensysteme GmbH.
0006  *
0007  *  See the file "skfddi.c" for further information.
0008  *
0009  *  The information in this file is provided "AS IS" without warranty.
0010  *
0011  ******************************************************************************/
0012 
0013 /*
0014     SMT ECM
0015     Entity Coordination Management
0016     Hardware independent state machine
0017 */
0018 
0019 /*
0020  * Hardware independent state machine implemantation
0021  * The following external SMT functions are referenced :
0022  *
0023  *      queue_event()
0024  *      smt_timer_start()
0025  *      smt_timer_stop()
0026  *
0027  *  The following external HW dependent functions are referenced :
0028  *      sm_pm_bypass_req()
0029  *      sm_pm_get_ls()
0030  * 
0031  *  The following HW dependent events are required :
0032  *      NONE
0033  *
0034  */
0035 
0036 #include "h/types.h"
0037 #include "h/fddi.h"
0038 #include "h/smc.h"
0039 
0040 #define KERNEL
0041 #include "h/smtstate.h"
0042 
0043 /*
0044  * FSM Macros
0045  */
0046 #define AFLAG   0x10
0047 #define GO_STATE(x) (smc->mib.fddiSMTECMState = (x)|AFLAG)
0048 #define ACTIONS_DONE()  (smc->mib.fddiSMTECMState &= ~AFLAG)
0049 #define ACTIONS(x)  (x|AFLAG)
0050 
0051 #define EC0_OUT     0           /* not inserted */
0052 #define EC1_IN      1           /* inserted */
0053 #define EC2_TRACE   2           /* tracing */
0054 #define EC3_LEAVE   3           /* leaving the ring */
0055 #define EC4_PATH_TEST   4           /* performing path test */
0056 #define EC5_INSERT  5           /* bypass being turned on */
0057 #define EC6_CHECK   6           /* checking bypass */
0058 #define EC7_DEINSERT    7           /* bypass being turnde off */
0059 
0060 /*
0061  * symbolic state names
0062  */
0063 static const char * const ecm_states[] = {
0064     "EC0_OUT","EC1_IN","EC2_TRACE","EC3_LEAVE","EC4_PATH_TEST",
0065     "EC5_INSERT","EC6_CHECK","EC7_DEINSERT"
0066 } ;
0067 
0068 /*
0069  * symbolic event names
0070  */
0071 static const char * const ecm_events[] = {
0072     "NONE","EC_CONNECT","EC_DISCONNECT","EC_TRACE_PROP","EC_PATH_TEST",
0073     "EC_TIMEOUT_TD","EC_TIMEOUT_TMAX",
0074     "EC_TIMEOUT_IMAX","EC_TIMEOUT_INMAX","EC_TEST_DONE"
0075 } ;
0076 
0077 /*
0078  * all Globals  are defined in smc.h
0079  * struct s_ecm
0080  */
0081 
0082 /*
0083  * function declarations
0084  */
0085 
0086 static void ecm_fsm(struct s_smc *smc, int cmd);
0087 static void start_ecm_timer(struct s_smc *smc, u_long value, int event);
0088 static void stop_ecm_timer(struct s_smc *smc);
0089 static void prop_actions(struct s_smc *smc);
0090 
0091 /*
0092     init ECM state machine
0093     clear all ECM vars and flags
0094 */
0095 void ecm_init(struct s_smc *smc)
0096 {
0097     smc->e.path_test = PT_PASSED ;
0098     smc->e.trace_prop = 0 ;
0099     smc->e.sb_flag = 0 ;
0100     smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ;
0101     smc->e.ecm_line_state = FALSE ;
0102 }
0103 
0104 /*
0105     ECM state machine
0106     called by dispatcher
0107 
0108     do
0109         display state change
0110         process event
0111     until SM is stable
0112 */
0113 void ecm(struct s_smc *smc, int event)
0114 {
0115     int state ;
0116 
0117     do {
0118         DB_ECM("ECM : state %s%s event %s",
0119                smc->mib.fddiSMTECMState & AFLAG ? "ACTIONS " : "",
0120                ecm_states[smc->mib.fddiSMTECMState & ~AFLAG],
0121                ecm_events[event]);
0122         state = smc->mib.fddiSMTECMState ;
0123         ecm_fsm(smc,event) ;
0124         event = 0 ;
0125     } while (state != smc->mib.fddiSMTECMState) ;
0126     ecm_state_change(smc,(int)smc->mib.fddiSMTECMState) ;
0127 }
0128 
0129 /*
0130     process ECM event
0131 */
0132 static void ecm_fsm(struct s_smc *smc, int cmd)
0133 {
0134     int ls_a ;          /* current line state PHY A */
0135     int ls_b ;          /* current line state PHY B */
0136     int p ;         /* ports */
0137 
0138 
0139     smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ;
0140     if (cmd == EC_CONNECT)
0141         smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ;
0142 
0143     /* For AIX event notification: */
0144     /* Is a disconnect  command remotely issued ? */
0145     if (cmd == EC_DISCONNECT &&
0146         smc->mib.fddiSMTRemoteDisconnectFlag == TRUE) {
0147         AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long)
0148             FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc),
0149             smt_get_error_word(smc) );
0150     }
0151 
0152     /*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/
0153     if (cmd == EC_CONNECT) {
0154         smc->e.DisconnectFlag = FALSE ;
0155     }
0156     else if (cmd == EC_DISCONNECT) {
0157         smc->e.DisconnectFlag = TRUE ;
0158     }
0159     
0160     switch(smc->mib.fddiSMTECMState) {
0161     case ACTIONS(EC0_OUT) :
0162         /*
0163          * We do not perform a path test
0164          */
0165         smc->e.path_test = PT_PASSED ;
0166         smc->e.ecm_line_state = FALSE ;
0167         stop_ecm_timer(smc) ;
0168         ACTIONS_DONE() ;
0169         break ;
0170     case EC0_OUT:
0171         /*EC01*/
0172         if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent
0173             && smc->e.path_test==PT_PASSED) {
0174             GO_STATE(EC1_IN) ;
0175             break ;
0176         }
0177         /*EC05*/
0178         else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) &&
0179             smc->mib.fddiSMTBypassPresent &&
0180             (smc->s.sas == SMT_DAS)) {
0181             GO_STATE(EC5_INSERT) ;
0182             break ;
0183         }
0184         break;
0185     case ACTIONS(EC1_IN) :
0186         stop_ecm_timer(smc) ;
0187         smc->e.trace_prop = 0 ;
0188         sm_ma_control(smc,MA_TREQ) ;
0189         for (p = 0 ; p < NUMPHYS ; p++)
0190             if (smc->mib.p[p].fddiPORTHardwarePresent)
0191                 queue_event(smc,EVENT_PCMA+p,PC_START) ;
0192         ACTIONS_DONE() ;
0193         break ;
0194     case EC1_IN:
0195         /*EC12*/
0196         if (cmd == EC_TRACE_PROP) {
0197             prop_actions(smc) ;
0198             GO_STATE(EC2_TRACE) ;
0199             break ;
0200         }
0201         /*EC13*/
0202         else if (cmd == EC_DISCONNECT) {
0203             GO_STATE(EC3_LEAVE) ;
0204             break ;
0205         }
0206         break;
0207     case ACTIONS(EC2_TRACE) :
0208         start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration),
0209             EC_TIMEOUT_TMAX) ;
0210         ACTIONS_DONE() ;
0211         break ;
0212     case EC2_TRACE :
0213         /*EC22*/
0214         if (cmd == EC_TRACE_PROP) {
0215             prop_actions(smc) ;
0216             GO_STATE(EC2_TRACE) ;
0217             break ;
0218         }
0219         /*EC23a*/
0220         else if (cmd == EC_DISCONNECT) {
0221             smc->e.path_test = PT_EXITING ;
0222             GO_STATE(EC3_LEAVE) ;
0223             break ;
0224         }
0225         /*EC23b*/
0226         else if (smc->e.path_test == PT_PENDING) {
0227             GO_STATE(EC3_LEAVE) ;
0228             break ;
0229         }
0230         /*EC23c*/
0231         else if (cmd == EC_TIMEOUT_TMAX) {
0232             /* Trace_Max is expired */
0233             /* -> send AIX_EVENT */
0234             AIX_EVENT(smc, (u_long) FDDI_RING_STATUS,
0235                 (u_long) FDDI_SMT_ERROR, (u_long)
0236                 FDDI_TRACE_MAX, smt_get_error_word(smc));
0237             smc->e.path_test = PT_PENDING ;
0238             GO_STATE(EC3_LEAVE) ;
0239             break ;
0240         }
0241         break ;
0242     case ACTIONS(EC3_LEAVE) :
0243         start_ecm_timer(smc,smc->s.ecm_td_min,EC_TIMEOUT_TD) ;
0244         for (p = 0 ; p < NUMPHYS ; p++)
0245             queue_event(smc,EVENT_PCMA+p,PC_STOP) ;
0246         ACTIONS_DONE() ;
0247         break ;
0248     case EC3_LEAVE:
0249         /*EC30*/
0250         if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent &&
0251             (smc->e.path_test != PT_PENDING)) {
0252             GO_STATE(EC0_OUT) ;
0253             break ;
0254         }
0255         /*EC34*/
0256         else if (cmd == EC_TIMEOUT_TD &&
0257             (smc->e.path_test == PT_PENDING)) {
0258             GO_STATE(EC4_PATH_TEST) ;
0259             break ;
0260         }
0261         /*EC31*/
0262         else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
0263             GO_STATE(EC1_IN) ;
0264             break ;
0265         }
0266         /*EC33*/
0267         else if (cmd == EC_DISCONNECT &&
0268             smc->e.path_test == PT_PENDING) {
0269             smc->e.path_test = PT_EXITING ;
0270             /*
0271              * stay in state - state will be left via timeout
0272              */
0273         }
0274         /*EC37*/
0275         else if (cmd == EC_TIMEOUT_TD &&
0276             smc->mib.fddiSMTBypassPresent &&
0277             smc->e.path_test != PT_PENDING) {
0278             GO_STATE(EC7_DEINSERT) ;
0279             break ;
0280         }
0281         break ;
0282     case ACTIONS(EC4_PATH_TEST) :
0283         stop_ecm_timer(smc) ;
0284         smc->e.path_test = PT_TESTING ;
0285         start_ecm_timer(smc,smc->s.ecm_test_done,EC_TEST_DONE) ;
0286         /* now perform path test ... just a simulation */
0287         ACTIONS_DONE() ;
0288         break ;
0289     case EC4_PATH_TEST :
0290         /* path test done delay */
0291         if (cmd == EC_TEST_DONE)
0292             smc->e.path_test = PT_PASSED ;
0293 
0294         if (smc->e.path_test == PT_FAILED)
0295             RS_SET(smc,RS_PATHTEST) ;
0296 
0297         /*EC40a*/
0298         if (smc->e.path_test == PT_FAILED &&
0299             !smc->mib.fddiSMTBypassPresent) {
0300             GO_STATE(EC0_OUT) ;
0301             break ;
0302         }
0303         /*EC40b*/
0304         else if (cmd == EC_DISCONNECT &&
0305             !smc->mib.fddiSMTBypassPresent) {
0306             GO_STATE(EC0_OUT) ;
0307             break ;
0308         }
0309         /*EC41*/
0310         else if (smc->e.path_test == PT_PASSED) {
0311             GO_STATE(EC1_IN) ;
0312             break ;
0313         }
0314         /*EC47a*/
0315         else if (smc->e.path_test == PT_FAILED &&
0316             smc->mib.fddiSMTBypassPresent) {
0317             GO_STATE(EC7_DEINSERT) ;
0318             break ;
0319         }
0320         /*EC47b*/
0321         else if (cmd == EC_DISCONNECT &&
0322             smc->mib.fddiSMTBypassPresent) {
0323             GO_STATE(EC7_DEINSERT) ;
0324             break ;
0325         }
0326         break ;
0327     case ACTIONS(EC5_INSERT) :
0328         sm_pm_bypass_req(smc,BP_INSERT);
0329         start_ecm_timer(smc,smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ;
0330         ACTIONS_DONE() ;
0331         break ;
0332     case EC5_INSERT :
0333         /*EC56*/
0334         if (cmd == EC_TIMEOUT_INMAX) {
0335             GO_STATE(EC6_CHECK) ;
0336             break ;
0337         }
0338         /*EC57*/
0339         else if (cmd == EC_DISCONNECT) {
0340             GO_STATE(EC7_DEINSERT) ;
0341             break ;
0342         }
0343         break ;
0344     case ACTIONS(EC6_CHECK) :
0345         /*
0346          * in EC6_CHECK, we *POLL* the line state !
0347          * check whether both bypass switches have switched.
0348          */
0349         start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
0350         smc->e.ecm_line_state = TRUE ;  /* flag to pcm: report Q/HLS */
0351         ACTIONS_DONE() ;
0352         break ;
0353     case EC6_CHECK :
0354         ls_a = sm_pm_get_ls(smc,PA) ;
0355         ls_b = sm_pm_get_ls(smc,PB) ;
0356 
0357         /*EC61*/
0358         if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) &&
0359             ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) {
0360             smc->e.sb_flag = FALSE ;
0361             smc->e.ecm_line_state = FALSE ;
0362             GO_STATE(EC1_IN) ;
0363             break ;
0364         }
0365         /*EC66*/
0366         else if (!smc->e.sb_flag &&
0367              (((ls_a == PC_ILS) && (ls_b == PC_QLS)) ||
0368               ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){
0369             smc->e.sb_flag = TRUE ;
0370             DB_ECMN(1, "ECM : EC6_CHECK - stuck bypass");
0371             AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long)
0372                 FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK,
0373                 smt_get_error_word(smc));
0374         }
0375         /*EC67*/
0376         else if (cmd == EC_DISCONNECT) {
0377             smc->e.ecm_line_state = FALSE ;
0378             GO_STATE(EC7_DEINSERT) ;
0379             break ;
0380         }
0381         else {
0382             /*
0383              * restart poll
0384              */
0385             start_ecm_timer(smc,smc->s.ecm_check_poll,0) ;
0386         }
0387         break ;
0388     case ACTIONS(EC7_DEINSERT) :
0389         sm_pm_bypass_req(smc,BP_DEINSERT);
0390         start_ecm_timer(smc,smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ;
0391         ACTIONS_DONE() ;
0392         break ;
0393     case EC7_DEINSERT:
0394         /*EC70*/
0395         if (cmd == EC_TIMEOUT_IMAX) {
0396             GO_STATE(EC0_OUT) ;
0397             break ;
0398         }
0399         /*EC75*/
0400         else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) {
0401             GO_STATE(EC5_INSERT) ;
0402             break ;
0403         }
0404         break;
0405     default:
0406         SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ;
0407         break;
0408     }
0409 }
0410 
0411 #ifndef CONCENTRATOR
0412 /*
0413  * trace propagation actions for SAS & DAS
0414  */
0415 static void prop_actions(struct s_smc *smc)
0416 {
0417     int port_in = 0 ;
0418     int port_out = 0 ;
0419 
0420     RS_SET(smc,RS_EVENT) ;
0421     switch (smc->s.sas) {
0422     case SMT_SAS :
0423         port_in = port_out = pcm_get_s_port(smc) ;
0424         break ;
0425     case SMT_DAS :
0426         port_in = cfm_get_mac_input(smc) ;  /* PA or PB */
0427         port_out = cfm_get_mac_output(smc) ;    /* PA or PB */
0428         break ;
0429     case SMT_NAC :
0430         SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ;
0431         return ;
0432     }
0433 
0434     DB_ECM("ECM : prop_actions - trace_prop %lu", smc->e.trace_prop);
0435     DB_ECM("ECM : prop_actions - in %d out %d", port_in, port_out);
0436 
0437     if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
0438         /* trace initiatior */
0439         DB_ECM("ECM : initiate TRACE on PHY %c", 'A' + port_in - PA);
0440         queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ;
0441     }
0442     else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) &&
0443         port_out != PA) {
0444         /* trace propagate upstream */
0445         DB_ECM("ECM : propagate TRACE on PHY B");
0446         queue_event(smc,EVENT_PCMB,PC_TRACE) ;
0447     }
0448     else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) &&
0449         port_out != PB) {
0450         /* trace propagate upstream */
0451         DB_ECM("ECM : propagate TRACE on PHY A");
0452         queue_event(smc,EVENT_PCMA,PC_TRACE) ;
0453     }
0454     else {
0455         /* signal trace termination */
0456         DB_ECM("ECM : TRACE terminated");
0457         smc->e.path_test = PT_PENDING ;
0458     }
0459     smc->e.trace_prop = 0 ;
0460 }
0461 #else
0462 /*
0463  * trace propagation actions for Concentrator
0464  */
0465 static void prop_actions(struct s_smc *smc)
0466 {
0467     int initiator ;
0468     int upstream ;
0469     int p ;
0470 
0471     RS_SET(smc,RS_EVENT) ;
0472     while (smc->e.trace_prop) {
0473         DB_ECM("ECM : prop_actions - trace_prop %d",
0474                smc->e.trace_prop);
0475 
0476         if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) {
0477             initiator = ENTITY_MAC ;
0478             smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ;
0479             DB_ECM("ECM: MAC initiates trace");
0480         }
0481         else {
0482             for (p = NUMPHYS-1 ; p >= 0 ; p--) {
0483                 if (smc->e.trace_prop &
0484                     ENTITY_BIT(ENTITY_PHY(p)))
0485                     break ;
0486             }
0487             initiator = ENTITY_PHY(p) ;
0488             smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ;
0489         }
0490         upstream = cem_get_upstream(smc,initiator) ;
0491 
0492         if (upstream == ENTITY_MAC) {
0493             /* signal trace termination */
0494             DB_ECM("ECM : TRACE terminated");
0495             smc->e.path_test = PT_PENDING ;
0496         }
0497         else {
0498             /* trace propagate upstream */
0499             DB_ECM("ECM : propagate TRACE on PHY %d", upstream);
0500             queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ;
0501         }
0502     }
0503 }
0504 #endif
0505 
0506 
0507 /*
0508  * SMT timer interface
0509  *  start ECM timer
0510  */
0511 static void start_ecm_timer(struct s_smc *smc, u_long value, int event)
0512 {
0513     smt_timer_start(smc,&smc->e.ecm_timer,value,EV_TOKEN(EVENT_ECM,event));
0514 }
0515 
0516 /*
0517  * SMT timer interface
0518  *  stop ECM timer
0519  */
0520 static void stop_ecm_timer(struct s_smc *smc)
0521 {
0522     if (smc->e.ecm_timer.tm_active)
0523         smt_timer_stop(smc,&smc->e.ecm_timer) ;
0524 }