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