Back to home page

LXR

 
 

    


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