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