!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/net/sk98lin/   drwxr-xr-x
Free 318.38 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     skrlmt.c (97.83 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/******************************************************************************
 *
 * Name:    skrlmt.c
 * Project:    GEnesis, PCI Gigabit Ethernet Adapter
 * Version:    $Revision: 1.61 $
 * Date:    $Date: 2001/03/14 12:52:08 $
 * Purpose:    Manage links on SK-NET Adapters, esp. redundant ones.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *    (C)Copyright 1998-2001 SysKonnect GmbH.
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    The information in this file is provided "AS IS" without warranty.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * History:
 *
 *    $Log: skrlmt.c,v $
 *    Revision 1.61  2001/03/14 12:52:08  rassmann
 *    Fixed reporting of active port up/down to PNMI.
 *    
 *    Revision 1.60  2001/02/21 16:02:25  gklug
 *    fix: when RLMT starts set Active Port for PNMI
 *    
 *    Revision 1.59  2001/02/16 14:38:19  rassmann
 *    Initializing some pointers earlier in the init phase.
 *    Rx Mbufs are freed if the net which they belong to is stopped.
 *    
 *    Revision 1.58  2001/02/14 14:06:31  rassmann
 *    Editorial changes.
 *    
 *    Revision 1.57  2001/02/05 14:25:26  rassmann
 *    Prepared RLMT for transparent operation.
 *    
 *    Revision 1.56  2001/01/30 10:29:09  rassmann
 *    Not checking switching befor RlmtStart.
 *    Editorial changes.
 *    
 *    Revision 1.55  2001/01/22 13:41:38  rassmann
 *    Supporting two nets on dual-port adapters.
 *    
 *    Revision 1.54  2000/11/30 13:25:07  rassmann
 *    Setting SK_TICK_INCR to 1 by default.
 *    
 *    Revision 1.53  2000/11/30 10:48:07  cgoos
 *    Changed definition of SK_RLMT_BC_DELTA.
 *    
 *    Revision 1.52  2000/11/27 12:50:03  rassmann
 *    Checking ports after receiving broadcasts.
 *    
 *    Revision 1.51  2000/11/17 08:58:00  rassmann
 *    Moved CheckSwitch from SK_RLMT_PACKET_RECEIVED to SK_RLMT_TIM event.
 *    
 *    Revision 1.50  2000/11/09 12:24:34  rassmann
 *    Indicating that segmentation check is not running anymore after
 *      SkRlmtCheckSeg().
 *    Restarting segmentation timer after segmentation log.
 *    Editorial changes.
 *    
 *    Revision 1.49  1999/11/22 13:38:02  cgoos
 *    Changed license header to GPL.
 *    Added initialization to some variables to avoid compiler warnings.
 *    
 *    Revision 1.48  1999/10/04 14:01:17  rassmann
 *    Corrected reaction to reception of BPDU frames (#10441).
 *    
 *    Revision 1.47  1999/07/20 12:53:36  rassmann
 *    Fixed documentation errors for lookahead macros.
 *    
 *    Revision 1.46  1999/05/28 13:29:16  rassmann
 *    Replaced C++-style comment.
 *    
 *    Revision 1.45  1999/05/28 13:28:08  rassmann
 *    Corrected syntax error (xxx).
 *    
 *    Revision 1.44  1999/05/28 11:15:54  rassmann
 *    Changed behaviour to reflect Design Spec v1.2.
 *    Controlling Link LED(s).
 *    Introduced RLMT Packet Version field in RLMT Packet.
 *    Newstyle lookahead macros (checking meta-information before looking at
 *      the packet).
 *    
 *    Revision 1.43  1999/01/28 13:12:43  rassmann
 *    Corrected Lookahead (bug introduced in previous Rev.).
 *    
 *    Revision 1.42  1999/01/28 12:50:41  rassmann
 *    Not using broadcast time stamps in CheckLinkState mode.
 *    
 *    Revision 1.41  1999/01/27 14:13:02  rassmann
 *    Monitoring broadcast traffic.
 *    Switching more reliably and not too early if switch is
 *     configured for spanning tree.
 *    
 *    Revision 1.40  1999/01/22 13:17:30  rassmann
 *    Informing PNMI of NET_UP.
 *    Clearing RLMT multicast addresses before setting them for the first time.
 *    Reporting segmentation earlier, setting a "quiet time"
 *     after a report.
 *    
 *    Revision 1.39  1998/12/10 15:29:53  rassmann
 *    Corrected SuspectStatus in SkRlmtBuildCheckChain().
 *    Corrected CHECK_SEG mode.
 *    
 *    Revision 1.38  1998/12/08 13:11:23  rassmann
 *    Stopping SegTimer at RlmtStop.
 *    
 *    Revision 1.37  1998/12/07 16:51:42  rassmann
 *    Corrected comments.
 *    
 *    Revision 1.36  1998/12/04 10:58:56  rassmann
 *    Setting next pointer to NULL when receiving.
 *    
 *    Revision 1.35  1998/12/03 16:12:42  rassmann
 *    Ignoring/correcting illegal PrefPort values.
 *    
 *    Revision 1.34  1998/12/01 11:45:35  rassmann
 *    Code cleanup.
 *    
 *    Revision 1.33  1998/12/01 10:29:32  rassmann
 *    Starting standby ports before getting the net up.
 *    Checking if a port is started when the link comes up.
 *    
 *    Revision 1.32  1998/11/30 16:19:50  rassmann
 *    New default for PortNoRx.
 *    
 *    Revision 1.31  1998/11/27 19:17:13  rassmann
 *    Corrected handling of LINK_DOWN coming shortly after LINK_UP.
 *    
 *    Revision 1.30  1998/11/24 12:37:31  rassmann
 *    Implemented segmentation check.
 *    
 *    Revision 1.29  1998/11/18 13:04:32  rassmann
 *    Secured PortUpTimer event.
 *    Waiting longer before starting standby port(s).
 *    
 *    Revision 1.28  1998/11/17 13:43:04  rassmann
 *    Handling (logical) tx failure.
 *    Sending packet on logical address after PORT_SWITCH.
 *    
 *    Revision 1.27  1998/11/13 17:09:50  rassmann
 *    Secured some events against being called in wrong state.
 *    
 *    Revision 1.26  1998/11/13 16:56:54  rassmann
 *    Added macro version of SkRlmtLookaheadPacket.
 *    
 *    Revision 1.25  1998/11/06 18:06:04  rassmann
 *    Corrected timing when RLMT checks fail.
 *    Clearing tx counter earlier in periodical checks.
 *    
 *    Revision 1.24  1998/11/05 10:37:27  rassmann
 *    Checking destination address in Lookahead.
 *    
 *    Revision 1.23  1998/11/03 13:53:49  rassmann
 *    RLMT should switch now (at least in mode 3).
 *    
 *    Revision 1.22  1998/10/29 14:34:49  rassmann
 *    Clearing SK_RLMT struct at startup.
 *    Initializing PortsUp during SK_RLMT_START.
 *    
 *    Revision 1.21  1998/10/28 11:30:17  rassmann
 *    Default mode is now SK_RLMT_CHECK_LOC_LINK.
 *    
 *    Revision 1.20  1998/10/26 16:02:03  rassmann
 *    Ignoring LINK_DOWN for links that are down.
 *    
 *    Revision 1.19  1998/10/22 15:54:01  rassmann
 *    Corrected EtherLen.
 *    Starting Link Check when second port comes up.
 *    
 *    Revision 1.18  1998/10/22 11:39:50  rassmann
 *    Corrected signed/unsigned mismatches.
 *    Corrected receive list handling and address recognition.
 *    
 *    Revision 1.17  1998/10/19 17:01:20  rassmann
 *    More detailed checking of received packets.
 *    
 *    Revision 1.16  1998/10/15 15:16:34  rassmann
 *    Finished Spanning Tree checking.
 *    Checked with lint.
 *    
 *    Revision 1.15  1998/09/24 19:16:07  rassmann
 *    Code cleanup.
 *    Introduced Timer for PORT_DOWN due to no RX.
 *    
 *    Revision 1.14  1998/09/18 20:27:14  rassmann
 *    Added address override.
 *    
 *    Revision 1.13  1998/09/16 11:31:48  rassmann
 *    Including skdrv1st.h again. :(
 *    
 *    Revision 1.12  1998/09/16 11:09:50  rassmann
 *    Syntax corrections.
 *    
 *    Revision 1.11  1998/09/15 12:32:03  rassmann
 *    Syntax correction.
 *    
 *    Revision 1.10  1998/09/15 11:28:49  rassmann
 *    Syntax corrections.
 *    
 *    Revision 1.9  1998/09/14 17:07:37  rassmann
 *    Added code for port checking via LAN.
 *    Changed Mbuf definition.
 *    
 *    Revision 1.8  1998/09/07 11:14:14  rassmann
 *    Syntax corrections.
 *    
 *    Revision 1.7  1998/09/07 09:06:07  rassmann
 *    Syntax corrections.
 *    
 *    Revision 1.6  1998/09/04 19:41:33  rassmann
 *    Syntax corrections.
 *    Started entering code for checking local links.
 *    
 *    Revision 1.5  1998/09/04 12:14:27  rassmann
 *    Interface cleanup.
 *    
 *    Revision 1.4  1998/09/02 16:55:28  rassmann
 *    Updated to reflect new DRV/HWAC/RLMT interface.
 *    
 *    Revision 1.3  1998/08/27 14:29:03  rassmann
 *    Code cleanup.
 *    
 *    Revision 1.2  1998/08/27 14:26:24  rassmann
 *    Updated interface.
 *    
 *    Revision 1.1  1998/08/21 08:26:49  rassmann
 *    First public version.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * Description:
 *
 * This module contains code for Link ManagemenT (LMT) of SK-NET Adapters.
 * It is mainly intended for adapters with more than one link.
 * For such adapters, this module realizes Redundant Link ManagemenT (RLMT).
 *
 * Include File Hierarchy:
 *
 *    "skdrv1st.h"
 *    "skdrv2nd.h"
 *
 ******************************************************************************/

#ifndef    lint
static const char SysKonnectFileId[] =
    "@(#) $Id: skrlmt.c,v 1.61 2001/03/14 12:52:08 rassmann Exp $ (C) SysKonnect.";
#endif    /* !defined(lint) */

#define __SKRLMT_C

#ifdef __cplusplus
#error C++ is not yet supported.
extern "C" {
#endif    /* cplusplus */

#include "h/skdrv1st.h"
#include "h/skdrv2nd.h"

/* defines ********************************************************************/

#ifndef SK_HWAC_LINK_LED
#define SK_HWAC_LINK_LED(a,b,c,d)
#endif    /* !defined(SK_HWAC_LINK_LED) */

#ifndef DEBUG
#define RLMT_STATIC    static
#else    /* DEBUG */
#define RLMT_STATIC

#ifndef SK_LITTLE_ENDIAN
/* First 32 bits */
#define OFFS_LO32    1

/* Second 32 bits */
#define OFFS_HI32    0
#else    /* SK_LITTLE_ENDIAN */
/* First 32 bits */
#define OFFS_LO32    0

/* Second 32 bits */
#define OFFS_HI32    1
#endif    /* SK_LITTLE_ENDIAN */

#endif    /* DEBUG */

/* ----- Private timeout values ----- */

#define SK_RLMT_MIN_TO_VAL               125000    /* 1/8 sec. */
#define SK_RLMT_DEF_TO_VAL              1000000    /* 1 sec. */
#define SK_RLMT_PORTDOWN_TIM_VAL       900000    /* another 0.9 sec. */
#define SK_RLMT_PORTSTART_TIM_VAL       100000    /* 0.1 sec. */
#define SK_RLMT_PORTUP_TIM_VAL          2500000    /* 2.5 sec. */
#define SK_RLMT_SEG_TO_VAL            900000000    /* 15 min. */

/* Assume tick counter increment is 1 - may be set OS-dependent. */
#ifndef SK_TICK_INCR
#define SK_TICK_INCR    SK_CONSTU64(1)
#endif    /* !defined(SK_TICK_INCR) */

/*
 * Amount that a time stamp must be later to be recognized as "substantially
 * later". This is about 1/128 sec, but above 1 tick counter increment.
 */
#define SK_RLMT_BC_DELTA        (1 + ((SK_TICKS_PER_SEC >> 7) > SK_TICK_INCR ? \
                                    (SK_TICKS_PER_SEC >> 7) : SK_TICK_INCR))

/* ----- Private RLMT defaults ----- */

#define SK_RLMT_DEF_PREF_PORT    0                    /* "Lower" port. */
#define SK_RLMT_DEF_MODE         SK_RLMT_CHECK_LINK    /* Default RLMT Mode. */

/* ----- Private RLMT checking states ----- */

#define SK_RLMT_RCS_SEG            1        /* RLMT Check State: check seg. */
#define SK_RLMT_RCS_START_SEG    2        /* RLMT Check State: start check seg. */
#define SK_RLMT_RCS_SEND_SEG    4        /* RLMT Check State: send BPDU packet */
#define SK_RLMT_RCS_REPORT_SEG    8        /* RLMT Check State: report seg. */

/* ----- Private PORT checking states ----- */

#define SK_RLMT_PCS_TX            1        /* Port Check State: check tx. */
#define SK_RLMT_PCS_RX            2        /* Port Check State: check rx. */

/* ----- Private PORT events ----- */

/* Note: Update simulation when changing these. */
#define SK_RLMT_PORTSTART_TIM    1100    /* Port start timeout. */
#define SK_RLMT_PORTUP_TIM        1101    /* Port can now go up. */
#define SK_RLMT_PORTDOWN_RX_TIM    1102    /* Port did not receive once ... */
#define SK_RLMT_PORTDOWN        1103    /* Port went down. */
#define SK_RLMT_PORTDOWN_TX_TIM    1104    /* Partner did not receive ... */

/* ----- Private RLMT events ----- */

/* Note: Update simulation when changing these. */
#define SK_RLMT_TIM                2100    /* RLMT timeout. */
#define SK_RLMT_SEG_TIM            2101    /* RLMT segmentation check timeout. */

#define TO_SHORTEN(tim)    ((tim) / 2)

/* Error numbers and messages. */
#define SKERR_RLMT_E001        (SK_ERRBASE_RLMT + 0)
#define SKERR_RLMT_E001_MSG    "No Packet."
#define SKERR_RLMT_E002        (SKERR_RLMT_E001 + 1)
#define SKERR_RLMT_E002_MSG    "Short Packet."
#define SKERR_RLMT_E003        (SKERR_RLMT_E002 + 1)
#define SKERR_RLMT_E003_MSG    "Unknown RLMT event."
#define SKERR_RLMT_E004        (SKERR_RLMT_E003 + 1)
#define SKERR_RLMT_E004_MSG    "PortsUp incorrect."
#define SKERR_RLMT_E005        (SKERR_RLMT_E004 + 1)
#define SKERR_RLMT_E005_MSG    \
 "Net seems to be segmented (different root bridges are reported on the ports)."
#define SKERR_RLMT_E006        (SKERR_RLMT_E005 + 1)
#define SKERR_RLMT_E006_MSG    "Duplicate MAC Address detected."
#define SKERR_RLMT_E007        (SKERR_RLMT_E006 + 1)
#define SKERR_RLMT_E007_MSG    "LinksUp incorrect."
#define SKERR_RLMT_E008        (SKERR_RLMT_E007 + 1)
#define SKERR_RLMT_E008_MSG    "Port not started but link came up."
#define SKERR_RLMT_E009        (SKERR_RLMT_E008 + 1)
#define SKERR_RLMT_E009_MSG    "Corrected illegal setting of Preferred Port."
#define SKERR_RLMT_E010        (SKERR_RLMT_E009 + 1)
#define SKERR_RLMT_E010_MSG    "Ignored illegal Preferred Port."

/* LLC field values. */
#define LLC_COMMAND_RESPONSE_BIT        1
#define LLC_TEST_COMMAND                0xE3
#define LLC_UI                            0x03

/* RLMT Packet fields. */
#define    SK_RLMT_DSAP                    0
#define    SK_RLMT_SSAP                    0
#define SK_RLMT_CTRL                    (LLC_TEST_COMMAND)
#define SK_RLMT_INDICATOR0                0x53    /* S */
#define SK_RLMT_INDICATOR1                0x4B    /* K */
#define SK_RLMT_INDICATOR2                0x2D    /* - */
#define SK_RLMT_INDICATOR3                0x52    /* R */
#define SK_RLMT_INDICATOR4                0x4C    /* L */
#define SK_RLMT_INDICATOR5                0x4D    /* M */
#define SK_RLMT_INDICATOR6                0x54    /* T */
#define SK_RLMT_PACKET_VERSION            0

/* RLMT SPT Flag values. */
#define    SK_RLMT_SPT_FLAG_CHANGE            0x01
#define    SK_RLMT_SPT_FLAG_CHANGE_ACK        0x80

/* RLMT SPT Packet fields. */
#define    SK_RLMT_SPT_DSAP                0x42
#define    SK_RLMT_SPT_SSAP                0x42
#define SK_RLMT_SPT_CTRL                (LLC_UI)
#define    SK_RLMT_SPT_PROTOCOL_ID0        0x00
#define    SK_RLMT_SPT_PROTOCOL_ID1        0x00
#define    SK_RLMT_SPT_PROTOCOL_VERSION_ID    0x00
#define    SK_RLMT_SPT_BPDU_TYPE            0x00
#define    SK_RLMT_SPT_FLAGS                0x00    /* ?? */
#define    SK_RLMT_SPT_ROOT_ID0            0xFF    /* Lowest possible priority. */
#define    SK_RLMT_SPT_ROOT_ID1            0xFF    /* Lowest possible priority. */

/* Remaining 6 bytes will be the current port address. */
#define    SK_RLMT_SPT_ROOT_PATH_COST0        0x00
#define    SK_RLMT_SPT_ROOT_PATH_COST1        0x00
#define    SK_RLMT_SPT_ROOT_PATH_COST2        0x00
#define    SK_RLMT_SPT_ROOT_PATH_COST3        0x00
#define    SK_RLMT_SPT_BRIDGE_ID0            0xFF    /* Lowest possible priority. */
#define    SK_RLMT_SPT_BRIDGE_ID1            0xFF    /* Lowest possible priority. */

/* Remaining 6 bytes will be the current port address. */
#define    SK_RLMT_SPT_PORT_ID0            0xFF    /* Lowest possible priority. */
#define    SK_RLMT_SPT_PORT_ID1            0xFF    /* Lowest possible priority. */
#define    SK_RLMT_SPT_MSG_AGE0            0x00
#define    SK_RLMT_SPT_MSG_AGE1            0x00
#define    SK_RLMT_SPT_MAX_AGE0            0x00
#define    SK_RLMT_SPT_MAX_AGE1            0xFF
#define    SK_RLMT_SPT_HELLO_TIME0            0x00
#define    SK_RLMT_SPT_HELLO_TIME1            0xFF
#define    SK_RLMT_SPT_FWD_DELAY0            0x00
#define    SK_RLMT_SPT_FWD_DELAY1            0x40

/* Size defines. */
#define SK_RLMT_MIN_PACKET_SIZE            34
#define SK_RLMT_MAX_PACKET_SIZE            (SK_RLMT_MAX_TX_BUF_SIZE)
#define SK_PACKET_DATA_LEN                (SK_RLMT_MAX_PACKET_SIZE - \
                                        SK_RLMT_MIN_PACKET_SIZE)

/* ----- RLMT packet types ----- */
#define SK_PACKET_ANNOUNCE                1    /* Port announcement. */
#define SK_PACKET_ALIVE                    2    /* Alive packet to port. */
#define SK_PACKET_ADDR_CHANGED            3    /* Port address changed. */
#define SK_PACKET_CHECK_TX                4    /* Check your tx line. */

#ifdef SK_LITTLE_ENDIAN
#define SK_U16_TO_NETWORK_ORDER(Val,Addr) { \
    SK_U8    *_Addr = (SK_U8*)(Addr); \
    SK_U16    _Val = (SK_U16)(Val); \
    *_Addr++ = (SK_U8)(_Val >> 8); \
    *_Addr = (SK_U8)(_Val & 0xFF); \
}
#endif    /* SK_LITTLE_ENDIAN */

#ifdef SK_BIG_ENDIAN
#define SK_U16_TO_NETWORK_ORDER(Val,Addr) (*(SK_U16*)(Addr) = (SK_U16)(Val))
#endif    /* SK_BIG_ENDIAN */

#define AUTONEG_FAILED    SK_FALSE
#define AUTONEG_SUCCESS    SK_TRUE


/* typedefs *******************************************************************/

/* RLMT packet.  Length: SK_RLMT_MAX_PACKET_SIZE (60) bytes. */
typedef struct s_RlmtPacket {
    SK_U8    DstAddr[SK_MAC_ADDR_LEN];
    SK_U8    SrcAddr[SK_MAC_ADDR_LEN];
    SK_U8    TypeLen[2];
    SK_U8    DSap;
    SK_U8    SSap;
    SK_U8    Ctrl;
    SK_U8    Indicator[7];
    SK_U8    RlmtPacketType[2];
    SK_U8    Align1[2];
    SK_U8    Random[4];                /* Random value of requesting(!) station. */
    SK_U8    RlmtPacketVersion[2];    /* RLMT Packet version. */
    SK_U8    Data[SK_PACKET_DATA_LEN];
} SK_RLMT_PACKET;

typedef struct s_SpTreeRlmtPacket {
    SK_U8    DstAddr[SK_MAC_ADDR_LEN];
    SK_U8    SrcAddr[SK_MAC_ADDR_LEN];
    SK_U8    TypeLen[2];
    SK_U8    DSap;
    SK_U8    SSap;
    SK_U8    Ctrl;
    SK_U8    ProtocolId[2];
    SK_U8    ProtocolVersionId;
    SK_U8    BpduType;
    SK_U8    Flags;
    SK_U8    RootId[8];
    SK_U8    RootPathCost[4];
    SK_U8    BridgeId[8];
    SK_U8    PortId[2];
    SK_U8    MessageAge[2];
    SK_U8    MaxAge[2];
    SK_U8    HelloTime[2];
    SK_U8    ForwardDelay[2];
} SK_SPTREE_PACKET;

/* global variables ***********************************************************/

SK_MAC_ADDR    SkRlmtMcAddr =    {{0x01,  0x00,  0x5A,  0x52,  0x4C,  0x4D}};
SK_MAC_ADDR    BridgeMcAddr =    {{0x01,  0x80,  0xC2,  0x00,  0x00,  0x00}};
SK_MAC_ADDR    BcAddr =         {{0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF}};

/* local variables ************************************************************/

/* None. */

/* functions ******************************************************************/

RLMT_STATIC void    SkRlmtCheckSwitch(
    SK_AC    *pAC,
    SK_IOC    IoC,
    SK_U32    NetIdx);
RLMT_STATIC void    SkRlmtCheckSeg(
    SK_AC    *pAC,
    SK_IOC    IoC,
    SK_U32    NetIdx);
RLMT_STATIC void    SkRlmtEvtSetNets(
    SK_AC        *pAC,
    SK_IOC        IoC,
    SK_EVPARA    Para);

/******************************************************************************
 *
 *    SkRlmtInit - initialize data, set state to init
 *
 * Description:
 *
 *    SK_INIT_DATA
 *    ============
 *
 *    This routine initializes all RLMT-related variables to a known state.
 *    The initial state is SK_RLMT_RS_INIT.
 *    All ports are initialized to SK_RLMT_PS_INIT.
 *
 *
 *    SK_INIT_IO
 *    ==========
 *
 *    Nothing.
 *
 *
 *    SK_INIT_RUN
 *    ===========
 *
 *    Determine the adapter's random value.
 *    Set the hw registers, the "logical MAC address", the
 *    RLMT multicast address, and eventually the BPDU multicast address.
 *
 * Context:
 *    init, pageable
 *
 * Returns:
 *    Nothing.
 */
void    SkRlmtInit(
SK_AC    *pAC,    /* Adapter Context */
SK_IOC    IoC,    /* I/O Context */
int        Level)    /* Initialization Level */
{
    SK_U32        i, j;
    SK_U64        Random;
    SK_EVPARA    Para;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_INIT,
        ("RLMT Init level %d.\n", Level))

    switch (Level) {
    case SK_INIT_DATA:    /* Initialize data structures. */
        SK_MEMSET((char *)&pAC->Rlmt, 0, sizeof(SK_RLMT));

        for (i = 0; i < SK_MAX_MACS; i++) {
            pAC->Rlmt.Port[i].PortState = SK_RLMT_PS_INIT;
            pAC->Rlmt.Port[i].LinkDown = SK_TRUE;
            pAC->Rlmt.Port[i].PortDown = SK_TRUE;
            pAC->Rlmt.Port[i].PortStarted = SK_FALSE;
            pAC->Rlmt.Port[i].PortNoRx = SK_FALSE;
            pAC->Rlmt.Port[i].RootIdSet = SK_FALSE;
            pAC->Rlmt.Port[i].PortNumber = i;
            pAC->Rlmt.Port[i].Net = &pAC->Rlmt.Net[0];
            pAC->Rlmt.Port[i].AddrPort = &pAC->Addr.Port[i];
        }

        pAC->Rlmt.NumNets = 1;
        for (i = 0; i < SK_MAX_NETS; i++) {
            pAC->Rlmt.Net[i].RlmtState = SK_RLMT_RS_INIT;
            pAC->Rlmt.Net[i].RootIdSet = SK_FALSE;
            pAC->Rlmt.Net[i].Preference = 0xFFFFFFFF;      /* Automatic. */
            pAC->Rlmt.Net[i].PrefPort = SK_RLMT_DEF_PREF_PORT;
            /* Just assuming. */
            pAC->Rlmt.Net[i].ActivePort = pAC->Rlmt.Net[i].PrefPort;
            pAC->Rlmt.Net[i].RlmtMode = SK_RLMT_DEF_MODE;
            pAC->Rlmt.Net[i].TimeoutValue = SK_RLMT_DEF_TO_VAL;
            pAC->Rlmt.Net[i].NetNumber = i;
        }

        pAC->Rlmt.Net[0].Port[0] = &pAC->Rlmt.Port[0];
        pAC->Rlmt.Net[0].Port[1] = &pAC->Rlmt.Port[1];
#if SK_MAX_NETS > 1
        pAC->Rlmt.Net[1].Port[0] = &pAC->Rlmt.Port[1];
#endif    /* SK_MAX_NETS > 1 */
        break;

    case SK_INIT_IO:    /* GIMacsFound first available here. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_INIT,
            ("RLMT: %d MACs were detected.\n", pAC->GIni.GIMacsFound))

        pAC->Rlmt.Net[0].NumPorts = pAC->GIni.GIMacsFound;

        /* Initialize HW registers? */
        if (pAC->GIni.GIMacsFound < 2) {
            Para.Para32[0] = SK_RLMT_MODE_CLS;
            Para.Para32[1] = 0;
            (void)SkRlmtEvent(pAC, IoC, SK_RLMT_MODE_CHANGE, Para);
        }
        break;

    case SK_INIT_RUN:
        /* Ensure RLMT is set to one net. */
        if (pAC->Rlmt.NumNets > 1) {
            Para.Para32[0] = 1;
            Para.Para32[1] = -1;
            SkRlmtEvtSetNets(pAC, IoC, Para);
        }

        for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
            Random = SkOsGetTime(pAC);
            *(SK_U32*)&pAC->Rlmt.Port[i].Random = *(SK_U32*)&Random;

            for (j = 0; j < 4; j++) {
                pAC->Rlmt.Port[i].Random[j] ^= pAC->Rlmt.Port[i].AddrPort->
                    CurrentMacAddress.a[SK_MAC_ADDR_LEN - 1 - j];
            }

            (void)SkAddrMcClear(pAC, IoC, i, SK_ADDR_PERMANENT | SK_MC_SW_ONLY);
            
            /* Add RLMT MC address. */
            (void)SkAddrMcAdd(pAC, IoC, i, &SkRlmtMcAddr, SK_ADDR_PERMANENT);

            if (pAC->Rlmt.Net[0].RlmtMode & SK_RLMT_CHECK_SEG) {
                /* Add BPDU MC address. */
                (void)SkAddrMcAdd(pAC, IoC, i, &BridgeMcAddr, SK_ADDR_PERMANENT);
            }

            (void)SkAddrMcUpdate(pAC, IoC, i);
        }
        break;

    default:    /* error */
        break;
    }
    return;
}    /* SkRlmtInit */


/******************************************************************************
 *
 *    SkRlmtBuildCheckChain - build the check chain
 *
 * Description:
 *    This routine builds the local check chain:
 *    - Each port that is up checks the next port.
 *    - The last port that is up checks the first port that is up.
 *
 * Notes:
 *    - Currently only local ports are considered when building the chain.
 *    - Currently the SuspectState is just reset;
 *      it would be better to save it ...
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtBuildCheckChain(
SK_AC    *pAC,    /* Adapter Context */
SK_U32    NetIdx)    /* Net Number */
{
    SK_U32            i;
    SK_U32            NumMacsUp;
    SK_RLMT_PORT *    FirstMacUp;
    SK_RLMT_PORT *    PrevMacUp;

    FirstMacUp    = NULL;
    PrevMacUp    = NULL;
    
    if (!(pAC->Rlmt.Net[NetIdx].RlmtMode & SK_RLMT_CHECK_LOC_LINK)) {
        for (i = 0; i < pAC->Rlmt.Net[i].NumPorts; i++) {
            pAC->Rlmt.Net[NetIdx].Port[i]->PortsChecked = 0;
        }
        return;    /* Done. */
    }
            
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SkRlmtBuildCheckChain.\n"))

    NumMacsUp = 0;

    for (i = 0; i < pAC->Rlmt.Net[NetIdx].NumPorts; i++) {
        pAC->Rlmt.Net[NetIdx].Port[i]->PortsChecked = 0;
        pAC->Rlmt.Net[NetIdx].Port[i]->PortsSuspect = 0;
        pAC->Rlmt.Net[NetIdx].Port[i]->CheckingState &=
            ~(SK_RLMT_PCS_RX | SK_RLMT_PCS_TX);

        /*
         * If more than two links are detected we should consider
         * checking at least two other ports:
         * 1. the next port that is not LinkDown and
         * 2. the next port that is not PortDown.
         */
        if (!pAC->Rlmt.Net[NetIdx].Port[i]->LinkDown) {
            if (NumMacsUp == 0) {
                FirstMacUp = pAC->Rlmt.Net[NetIdx].Port[i];
            }
            else {
                pAC->Rlmt.Net[NetIdx].Port[i]->PortCheck[
                    pAC->Rlmt.Net[NetIdx].Port[i]->PortsChecked].CheckAddr =
                    pAC->Rlmt.Net[NetIdx].Port[i]->AddrPort->CurrentMacAddress;
                PrevMacUp->PortCheck[
                    PrevMacUp->PortsChecked].SuspectTx = SK_FALSE;
                PrevMacUp->PortsChecked++;
            }
            PrevMacUp = pAC->Rlmt.Net[NetIdx].Port[i];
            NumMacsUp++;
        }
    }

    if (NumMacsUp > 1) {
        PrevMacUp->PortCheck[PrevMacUp->PortsChecked].CheckAddr =
            FirstMacUp->AddrPort->CurrentMacAddress;
        PrevMacUp->PortCheck[PrevMacUp->PortsChecked].SuspectTx =
            SK_FALSE;
        PrevMacUp->PortsChecked++;
    }

#ifdef DEBUG
    for (i = 0; i < pAC->Rlmt.Net[NetIdx].NumPorts; i++) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Port %d checks %d other ports: %2X.\n", NetIdx,
                pAC->Rlmt.Net[NetIdx].Port[i]->PortsChecked,
                pAC->Rlmt.Net[NetIdx].Port[i]->PortCheck[0].CheckAddr.a[5]))
    }
#endif    /* DEBUG */

    return;       
}    /* SkRlmtBuildCheckChain */


/******************************************************************************
 *
 *    SkRlmtBuildPacket - build an RLMT packet
 *
 * Description:
 *    This routine sets up an RLMT packet.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    NULL or pointer to RLMT mbuf
 */
RLMT_STATIC SK_MBUF    *SkRlmtBuildPacket(
SK_AC        *pAC,        /* Adapter Context */
SK_IOC        IoC,        /* I/O Context */
SK_U32        PortNumber,    /* Sending port */
SK_U16        PacketType,    /* RLMT packet type */
SK_MAC_ADDR    *SrcAddr,    /* Source address */
SK_MAC_ADDR    *DestAddr)    /* Destination address */
{
    int        i;
    SK_U16        Length;
    SK_MBUF        *pMb;
    SK_RLMT_PACKET    *pPacket;

    if ((pMb = SkDrvAllocRlmtMbuf(pAC, IoC, SK_RLMT_MAX_PACKET_SIZE)) != NULL) {
        pPacket = (SK_RLMT_PACKET*)pMb->pData;
        for (i = 0; i < SK_MAC_ADDR_LEN; i++) {
            pPacket->DstAddr[i] = DestAddr->a[i];
            pPacket->SrcAddr[i] = SrcAddr->a[i];
        }
        pPacket->DSap = SK_RLMT_DSAP;
        pPacket->SSap = SK_RLMT_SSAP;
        pPacket->Ctrl = SK_RLMT_CTRL;
        pPacket->Indicator[0] = SK_RLMT_INDICATOR0;
        pPacket->Indicator[1] = SK_RLMT_INDICATOR1;
        pPacket->Indicator[2] = SK_RLMT_INDICATOR2;
        pPacket->Indicator[3] = SK_RLMT_INDICATOR3;
        pPacket->Indicator[4] = SK_RLMT_INDICATOR4;
        pPacket->Indicator[5] = SK_RLMT_INDICATOR5;
        pPacket->Indicator[6] = SK_RLMT_INDICATOR6;

        SK_U16_TO_NETWORK_ORDER(PacketType, &pPacket->RlmtPacketType[0]);

        for (i = 0; i < 4; i++) {
            pPacket->Random[i] = pAC->Rlmt.Port[PortNumber].Random[i];
        }
        
        SK_U16_TO_NETWORK_ORDER(
            SK_RLMT_PACKET_VERSION, &pPacket->RlmtPacketVersion[0]);

        for (i = 0; i < SK_PACKET_DATA_LEN; i++) {
            pPacket->Data[i] = 0x00;
        }

        Length = SK_RLMT_MAX_PACKET_SIZE;    /* Or smaller. */
        pMb->Length = Length;
        pMb->PortIdx = PortNumber;
        Length -= 14;
        SK_U16_TO_NETWORK_ORDER(Length, &pPacket->TypeLen[0]);

        if (PacketType == SK_PACKET_ALIVE) {
            pAC->Rlmt.Port[PortNumber].TxHelloCts++;
        }
    }

    return (pMb);       
}    /* SkRlmtBuildPacket */


/******************************************************************************
 *
 *    SkRlmtBuildSpanningTreePacket - build spanning tree check packet
 *
 * Description:
 *    This routine sets up a BPDU packet for spanning tree check.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    NULL or pointer to RLMT mbuf
 */
RLMT_STATIC SK_MBUF    *SkRlmtBuildSpanningTreePacket(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    PortNumber)    /* Sending port */
{
    unsigned            i;
    SK_U16                Length;
    SK_MBUF                *pMb;
    SK_SPTREE_PACKET    *pSPacket;

    if ((pMb = SkDrvAllocRlmtMbuf(pAC, IoC, SK_RLMT_MAX_PACKET_SIZE)) !=
        NULL) {
        pSPacket = (SK_SPTREE_PACKET*)pMb->pData;
        for (i = 0; i < SK_MAC_ADDR_LEN; i++) {
            pSPacket->DstAddr[i] = BridgeMcAddr.a[i];
            pSPacket->SrcAddr[i] =
                pAC->Addr.Port[PortNumber].CurrentMacAddress.a[i];
        }
        pSPacket->DSap = SK_RLMT_SPT_DSAP;
        pSPacket->SSap = SK_RLMT_SPT_SSAP;
        pSPacket->Ctrl = SK_RLMT_SPT_CTRL;

        pSPacket->ProtocolId[0] = SK_RLMT_SPT_PROTOCOL_ID0;
        pSPacket->ProtocolId[1] = SK_RLMT_SPT_PROTOCOL_ID1;
        pSPacket->ProtocolVersionId = SK_RLMT_SPT_PROTOCOL_VERSION_ID;
        pSPacket->BpduType = SK_RLMT_SPT_BPDU_TYPE;
        pSPacket->Flags = SK_RLMT_SPT_FLAGS;
        pSPacket->RootId[0] = SK_RLMT_SPT_ROOT_ID0;
        pSPacket->RootId[1] = SK_RLMT_SPT_ROOT_ID1;
        pSPacket->RootPathCost[0] = SK_RLMT_SPT_ROOT_PATH_COST0;
        pSPacket->RootPathCost[1] = SK_RLMT_SPT_ROOT_PATH_COST1;
        pSPacket->RootPathCost[2] = SK_RLMT_SPT_ROOT_PATH_COST2;
        pSPacket->RootPathCost[3] = SK_RLMT_SPT_ROOT_PATH_COST3;
        pSPacket->BridgeId[0] = SK_RLMT_SPT_BRIDGE_ID0;
        pSPacket->BridgeId[1] = SK_RLMT_SPT_BRIDGE_ID1;

        /*
         * Use logical MAC address as bridge ID and filter these packets
         * on receive.
         */
        for (i = 0; i < SK_MAC_ADDR_LEN; i++) {
            pSPacket->BridgeId[i + 2] = pSPacket->RootId[i + 2] =
                pAC->Addr.Net[pAC->Rlmt.Port[PortNumber].Net->NetNumber].
                    CurrentMacAddress.a[i];
        }
        pSPacket->PortId[0] = SK_RLMT_SPT_PORT_ID0;
        pSPacket->PortId[1] = SK_RLMT_SPT_PORT_ID1;
        pSPacket->MessageAge[0] = SK_RLMT_SPT_MSG_AGE0;
        pSPacket->MessageAge[1] = SK_RLMT_SPT_MSG_AGE1;
        pSPacket->MaxAge[0] = SK_RLMT_SPT_MAX_AGE0;
        pSPacket->MaxAge[1] = SK_RLMT_SPT_MAX_AGE1;
        pSPacket->HelloTime[0] = SK_RLMT_SPT_HELLO_TIME0;
        pSPacket->HelloTime[1] = SK_RLMT_SPT_HELLO_TIME1;
        pSPacket->ForwardDelay[0] = SK_RLMT_SPT_FWD_DELAY0;
        pSPacket->ForwardDelay[1] = SK_RLMT_SPT_FWD_DELAY1;

        Length = SK_RLMT_MAX_PACKET_SIZE;    /* Or smaller. */
        pMb->Length = Length;
        pMb->PortIdx = PortNumber;
        Length -= 14;
        SK_U16_TO_NETWORK_ORDER(Length, &pSPacket->TypeLen[0]);

        pAC->Rlmt.Port[PortNumber].TxSpHelloReqCts++;
    }

    return (pMb);       
}    /* SkRlmtBuildSpanningTreePacket */


/******************************************************************************
 *
 *    SkRlmtSend - build and send check packets
 *
 * Description:
 *    Depending on the RLMT state and the checking state, several packets
 *    are sent through the indicated port.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing.
 */
RLMT_STATIC void    SkRlmtSend(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    PortNumber)    /* Sending port */
{
    unsigned    j;
    SK_EVPARA    Para;
    SK_RLMT_PORT    *pRPort;

    pRPort = &pAC->Rlmt.Port[PortNumber];
    if (pAC->Rlmt.Port[PortNumber].Net->RlmtMode & SK_RLMT_CHECK_LOC_LINK) {
        if (pRPort->CheckingState & (SK_RLMT_PCS_TX | SK_RLMT_PCS_RX)) {
            /* Port is suspicious. Send the RLMT packet to the RLMT mc addr. */
            if ((Para.pParaPtr = SkRlmtBuildPacket(pAC, IoC, PortNumber,
                SK_PACKET_ALIVE, &pAC->Addr.Port[PortNumber].CurrentMacAddress,
                &SkRlmtMcAddr)) != NULL) {
                SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
            }
        }
        else {
            /*
             * Send a directed RLMT packet to all ports that are
             * checked by the indicated port.
             */
            for (j = 0; j < pRPort->PortsChecked; j++) {
                if ((Para.pParaPtr = SkRlmtBuildPacket(pAC, IoC, PortNumber,
                    SK_PACKET_ALIVE, &pAC->Addr.Port[PortNumber].CurrentMacAddress,
                    &pRPort->PortCheck[j].CheckAddr)) != NULL) {
                    SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
                }
            }
        }
    }

    if ((pAC->Rlmt.Port[PortNumber].Net->RlmtMode & SK_RLMT_CHECK_SEG) &&
        (pAC->Rlmt.Port[PortNumber].Net->CheckingState & SK_RLMT_RCS_SEND_SEG)) {
        /*
         * Send a BPDU packet to make a connected switch tell us
         * the correct root bridge.
         */
        if ((Para.pParaPtr =
            SkRlmtBuildSpanningTreePacket(pAC, IoC, PortNumber)) != NULL) {
            pAC->Rlmt.Port[PortNumber].Net->CheckingState &= ~SK_RLMT_RCS_SEND_SEG;
            pRPort->RootIdSet = SK_FALSE;

            SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_TX,
                ("SkRlmtSend: BPDU Packet on Port %u.\n", PortNumber))
        }
    }   
    return;   
}    /* SkRlmtSend */


/******************************************************************************
 *
 *    SkRlmtPortReceives - check if port is (going) down and bring it up
 *
 * Description:
 *    This routine checks if a port who received a non-BPDU packet
 *    needs to go up or needs to be stopped going down.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing.
 */
RLMT_STATIC void    SkRlmtPortReceives(
SK_AC    *pAC,            /* Adapter Context */
SK_IOC    IoC,            /* I/O Context */
SK_U32    PortNumber)        /* Port to check */
{
    SK_RLMT_PORT    *pRPort;
    SK_EVPARA        Para;

    pRPort = &pAC->Rlmt.Port[PortNumber];
    pRPort->PortNoRx = SK_FALSE;

    if ((pRPort->PortState == SK_RLMT_PS_DOWN) &&
        !(pRPort->CheckingState & SK_RLMT_PCS_TX)) {
        /*
         * Port is marked down (rx), but received a non-BPDU packet.
         * Bring it up.
         */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
            ("SkRlmtPacketReceive: Received on PortDown.\n"))

        pRPort->PortState = SK_RLMT_PS_GOING_UP;
        pRPort->GuTimeStamp = SkOsGetTime(pAC);
        Para.Para32[0] = PortNumber;
        Para.Para32[1] = (SK_U32)-1;
        SkTimerStart(pAC, IoC, &pRPort->UpTimer, SK_RLMT_PORTUP_TIM_VAL,
            SKGE_RLMT, SK_RLMT_PORTUP_TIM, Para);
        pRPort->CheckingState &= ~SK_RLMT_PCS_RX;
        /* pAC->Rlmt.CheckSwitch = SK_TRUE; */
        SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);
    }    /* PortDown && !SuspectTx */
    else if (pRPort->CheckingState & SK_RLMT_PCS_RX) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
            ("SkRlmtPacketReceive: Stop bringing port down.\n"))
        SkTimerStop(pAC, IoC, &pRPort->DownRxTimer);
        pRPort->CheckingState &= ~SK_RLMT_PCS_RX;
        /* pAC->Rlmt.CheckSwitch = SK_TRUE; */
        SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);
    }    /* PortGoingDown */

    return;
}    /* SkRlmtPortReceives */


/******************************************************************************
 *
 *    SkRlmtPacketReceive - receive a packet for closer examination
 *
 * Description:
 *    This routine examines a packet more closely than SK_RLMT_LOOKAHEAD.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing.
 */
RLMT_STATIC void    SkRlmtPacketReceive(
SK_AC    *pAC,    /* Adapter Context */
SK_IOC    IoC,    /* I/O Context */
SK_MBUF    *pMb)    /* Received packet */
{
#ifdef xDEBUG
    extern    void DumpData(char *p, int size);
#endif    /* DEBUG */
    int                    i;
    unsigned            j;
    SK_U16                PacketType;
    SK_U32                PortNumber;
    SK_ADDR_PORT        *pAPort;
    SK_RLMT_PORT        *pRPort;
    SK_RLMT_PACKET        *pRPacket;
    SK_SPTREE_PACKET    *pSPacket;
    SK_EVPARA            Para;

    PortNumber    = pMb->PortIdx;
    pAPort = &pAC->Addr.Port[PortNumber];
    pRPort = &pAC->Rlmt.Port[PortNumber];

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
        ("SkRlmtPacketReceive: PortNumber == %d.\n", PortNumber))

    pRPacket = (SK_RLMT_PACKET*)pMb->pData;
    pSPacket = (SK_SPTREE_PACKET*)pRPacket;

#ifdef xDEBUG
    DumpData((char *)pRPacket, 32);
#endif    /* DEBUG */

    if ((pRPort->PacketsPerTimeSlot - pRPort->BpduPacketsPerTimeSlot) != 0) {
        SkRlmtPortReceives(pAC, IoC, PortNumber);
    }
    
    /* Check destination address. */

    if (!SK_ADDR_EQUAL(pAPort->CurrentMacAddress.a, pRPacket->DstAddr) &&
        !SK_ADDR_EQUAL(SkRlmtMcAddr.a, pRPacket->DstAddr) &&
        !SK_ADDR_EQUAL(BridgeMcAddr.a, pRPacket->DstAddr)) {

        /* Not sent to current MAC or registered MC address => Trash it. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
            ("SkRlmtPacketReceive: Not for me.\n"))

        SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
        return;
    }
    else if (SK_ADDR_EQUAL(pAPort->CurrentMacAddress.a, pRPacket->SrcAddr)) {

        /*
         * Was sent by same port (may happen during port switching
         * or in case of duplicate MAC addresses).
         */

        /*
         * Check for duplicate address here:
         * If Packet.Random != My.Random => DupAddr.
         */
        for (i = 3; i >= 0; i--) {
            if (pRPort->Random[i] != pRPacket->Random[i]) {
                break;
            }
        }

        /*
         * CAUTION: Do not check for duplicate MAC address in RLMT Alive Reply
         * packets (they have the LLC_COMMAND_RESPONSE_BIT set in
         * pRPacket->SSap).
         */
        if (i >= 0 && pRPacket->DSap == SK_RLMT_DSAP &&
            pRPacket->Ctrl == SK_RLMT_CTRL &&
            pRPacket->SSap == SK_RLMT_SSAP &&
            pRPacket->Indicator[0] == SK_RLMT_INDICATOR0 &&
            pRPacket->Indicator[1] == SK_RLMT_INDICATOR1 &&
            pRPacket->Indicator[2] == SK_RLMT_INDICATOR2 &&
            pRPacket->Indicator[3] == SK_RLMT_INDICATOR3 &&
            pRPacket->Indicator[4] == SK_RLMT_INDICATOR4 &&
            pRPacket->Indicator[5] == SK_RLMT_INDICATOR5 &&
            pRPacket->Indicator[6] == SK_RLMT_INDICATOR6) {
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Duplicate MAC Address.\n"))

            /* Error Log entry. */
            SK_ERR_LOG(pAC, SK_ERRCL_COMM, SKERR_RLMT_E006, SKERR_RLMT_E006_MSG);
        }
        else {
            /* Simply trash it. */
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Sent by me.\n"))
        }

        SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
        return;
    }

    /* Check SuspectTx entries. */
    if (pRPort->PortsSuspect > 0) {
        for (j = 0; j < pRPort->PortsChecked; j++) {
            if (pRPort->PortCheck[j].SuspectTx &&
                SK_ADDR_EQUAL(
                    pRPacket->SrcAddr, pRPort->PortCheck[j].CheckAddr.a)) {
                pRPort->PortCheck[j].SuspectTx = SK_FALSE;
                pRPort->PortsSuspect--;
                break;
            }
        }
    }

    /* Determine type of packet. */
    if (pRPacket->DSap == SK_RLMT_DSAP &&
        pRPacket->Ctrl == SK_RLMT_CTRL &&
        (pRPacket->SSap & ~LLC_COMMAND_RESPONSE_BIT) == SK_RLMT_SSAP &&
        pRPacket->Indicator[0] == SK_RLMT_INDICATOR0 &&
        pRPacket->Indicator[1] == SK_RLMT_INDICATOR1 &&
        pRPacket->Indicator[2] == SK_RLMT_INDICATOR2 &&
        pRPacket->Indicator[3] == SK_RLMT_INDICATOR3 &&
        pRPacket->Indicator[4] == SK_RLMT_INDICATOR4 &&
        pRPacket->Indicator[5] == SK_RLMT_INDICATOR5 &&
        pRPacket->Indicator[6] == SK_RLMT_INDICATOR6) {

        /* It's an RLMT packet. */
        PacketType = (SK_U16)((pRPacket->RlmtPacketType[0] << 8) |
            pRPacket->RlmtPacketType[1]);

        switch (PacketType) {
        case SK_PACKET_ANNOUNCE:    /* Not yet used. */
#if 0
            /* Build the check chain. */
            SkRlmtBuildCheckChain(pAC);
#endif    /* 0 */

            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Announce.\n"))

            SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
            break;

        case SK_PACKET_ALIVE:
            if (pRPacket->SSap & LLC_COMMAND_RESPONSE_BIT) {
                SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                    ("SkRlmtPacketReceive: Alive Reply.\n"))

                if (!(pAC->Addr.Port[PortNumber].PromMode & SK_PROM_MODE_LLC) ||
                    SK_ADDR_EQUAL(
                        pRPacket->DstAddr, pAPort->CurrentMacAddress.a)) {
                    /* Obviously we could send something. */
                    if (pRPort->CheckingState & SK_RLMT_PCS_TX) {
                        pRPort->CheckingState &=  ~SK_RLMT_PCS_TX;
                        SkTimerStop(pAC, IoC, &pRPort->DownTxTimer);
                    }

                    if ((pRPort->PortState == SK_RLMT_PS_DOWN) &&
                        !(pRPort->CheckingState & SK_RLMT_PCS_RX)) {
                        pRPort->PortState = SK_RLMT_PS_GOING_UP;
                        pRPort->GuTimeStamp = SkOsGetTime(pAC);

                        SkTimerStop(pAC, IoC, &pRPort->DownTxTimer);

                        Para.Para32[0] = PortNumber;
                        Para.Para32[1] = (SK_U32)-1;
                        SkTimerStart(pAC, IoC, &pRPort->UpTimer,
                            SK_RLMT_PORTUP_TIM_VAL, SKGE_RLMT,
                            SK_RLMT_PORTUP_TIM, Para);
                    }
                }

                /* Mark sending port as alive? */
                SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
            }
            else {    /* Alive Request Packet. */
                SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                    ("SkRlmtPacketReceive: Alive Request.\n"))

                pRPort->RxHelloCts++;

                /* Answer. */
                for (i = 0; i < SK_MAC_ADDR_LEN; i++) {
                    pRPacket->DstAddr[i] = pRPacket->SrcAddr[i];
                    pRPacket->SrcAddr[i] =
                        pAC->Addr.Port[PortNumber].CurrentMacAddress.a[i];
                }
                pRPacket->SSap |= LLC_COMMAND_RESPONSE_BIT;

                Para.pParaPtr = pMb;
                SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
            }
            break;

        case SK_PACKET_CHECK_TX:
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Check your tx line.\n"))

            /* A port checking us requests us to check our tx line. */
            pRPort->CheckingState |= SK_RLMT_PCS_TX;

            /* Start PortDownTx timer. */
            Para.Para32[0] = PortNumber;
            Para.Para32[1] = (SK_U32)-1;
            SkTimerStart(pAC, IoC, &pRPort->DownTxTimer,
                SK_RLMT_PORTDOWN_TIM_VAL, SKGE_RLMT,
                SK_RLMT_PORTDOWN_TX_TIM, Para);

            SkDrvFreeRlmtMbuf(pAC, IoC, pMb);

            if ((Para.pParaPtr = SkRlmtBuildPacket(pAC, IoC, PortNumber,
                SK_PACKET_ALIVE, &pAC->Addr.Port[PortNumber].CurrentMacAddress,
                &SkRlmtMcAddr)) != NULL) {
                SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
            }
            break;

        case SK_PACKET_ADDR_CHANGED:
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Address Change.\n"))

            /* Build the check chain. */
            SkRlmtBuildCheckChain(pAC, pRPort->Net->NetNumber);
            SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
            break;

        default:
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
                ("SkRlmtPacketReceive: Unknown RLMT packet.\n"))

            /* RA;:;: ??? */
            SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
        }
    }
    else if (pSPacket->DSap == SK_RLMT_SPT_DSAP &&
        pSPacket->Ctrl == SK_RLMT_SPT_CTRL &&
        (pSPacket->SSap & ~LLC_COMMAND_RESPONSE_BIT) == SK_RLMT_SPT_SSAP) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
            ("SkRlmtPacketReceive: BPDU Packet.\n"))

        /* Spanning Tree packet. */
        pRPort->RxSpHelloCts++;

        if (!SK_ADDR_EQUAL(&pSPacket->RootId[2], &pAC->Addr.Net[pAC->Rlmt.
            Port[PortNumber].Net->NetNumber].CurrentMacAddress.a[0])) {
            /*
             * Check segmentation if a new root bridge is set and
             * the segmentation check is not currently running.
             */
            if (!SK_ADDR_EQUAL(&pSPacket->RootId[2], &pRPort->Root.Id[2]) &&
                (pAC->Rlmt.Port[PortNumber].Net->LinksUp > 1) &&
                (pAC->Rlmt.Port[PortNumber].Net->RlmtMode & SK_RLMT_CHECK_SEG)
                != 0 && (pAC->Rlmt.Port[PortNumber].Net->CheckingState &
                SK_RLMT_RCS_SEG) == 0) {
                pAC->Rlmt.Port[PortNumber].Net->CheckingState |=
                    SK_RLMT_RCS_START_SEG | SK_RLMT_RCS_SEND_SEG;
            }

            /* Store tree view of this port. */
            for (i = 0; i < 8; i++) {
                pRPort->Root.Id[i] = pSPacket->RootId[i];
            }
            pRPort->RootIdSet = SK_TRUE;

            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_DUMP,
                ("Root ID %d: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
                    PortNumber,
                    pRPort->Root.Id[0], pRPort->Root.Id[1],
                    pRPort->Root.Id[2], pRPort->Root.Id[3],
                    pRPort->Root.Id[4], pRPort->Root.Id[5],
                    pRPort->Root.Id[6], pRPort->Root.Id[7]))      
        }

        SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
        if ((pAC->Rlmt.Port[PortNumber].Net->CheckingState &
            SK_RLMT_RCS_REPORT_SEG) != 0) {
            SkRlmtCheckSeg(pAC, IoC, pAC->Rlmt.Port[PortNumber].Net->NetNumber);
        }
    }
    else {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_RX,
            ("SkRlmtPacketReceive: Unknown Packet Type.\n"))

        /* Unknown packet. */
        SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
    }
    return;
}    /* SkRlmtPacketReceive */


/******************************************************************************
 *
 *    SkRlmtCheckPort - check if a port works
 *
 * Description:
 *    This routine checks if a port whose link is up received something
 *    and if it seems to transmit successfully.
 *
 *    # PortState: PsInit, PsLinkDown, PsDown, PsGoingUp, PsUp
 *    # PortCheckingState (Bitfield): ChkTx, ChkRx, ChkSeg
 *    # RlmtCheckingState (Bitfield): ChkSeg, StartChkSeg, ReportSeg
 *
 *    if (Rx - RxBpdu == 0) {    # No rx.
 *        if (state == PsUp) {
 *            PortCheckingState |= ChkRx
 *        }
 *        if (ModeCheckSeg && (Timeout ==
 *            TO_SHORTEN(RLMT_DEFAULT_TIMEOUT))) {
 *            RlmtCheckingState |= ChkSeg)
 *            PortCheckingState |= ChkSeg
 *        }
 *        NewTimeout = TO_SHORTEN(Timeout)
 *        if (NewTimeout < RLMT_MIN_TIMEOUT) {
 *            NewTimeout = RLMT_MIN_TIMEOUT
 *            PortState = PsDown
 *            ...
 *        }
 *    }
 *    else {    # something was received
 *        # Set counter to 0 at LinkDown?
 *        #   No - rx may be reported after LinkDown ???
 *        PortCheckingState &= ~ChkRx
 *        NewTimeout = RLMT_DEFAULT_TIMEOUT
 *        if (RxAck == 0) {
 *            possible reasons:
 *            is my tx line bad? --
 *                send RLMT multicast and report
 *                back internally? (only possible
 *                between ports on same adapter)
 *        }
 *        if (RxChk == 0) {
 *            possible reasons:
 *            - tx line of port set to check me
 *              maybe bad
 *            - no other port/adapter available or set
 *              to check me
 *            - adapter checking me has a longer
 *              timeout
 *            ??? anything that can be done here?
 *        }
 *    }
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    New timeout value.
 */
RLMT_STATIC SK_U32    SkRlmtCheckPort(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    PortNumber)    /* Port to check */
{
    unsigned        i;
    SK_U32            NewTimeout;
    SK_RLMT_PORT    *pRPort;
    SK_EVPARA        Para;

    pRPort = &pAC->Rlmt.Port[PortNumber];

    if ((pRPort->PacketsPerTimeSlot - pRPort->BpduPacketsPerTimeSlot) == 0) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SkRlmtCheckPort %d: No (%d) receives in last time slot.\n",
                PortNumber, pRPort->PacketsPerTimeSlot))

        /*
         * Check segmentation if there was no receive at least twice
         * in a row (PortNoRx is already set) and the segmentation
         * check is not currently running.
         */

        if (pRPort->PortNoRx && (pAC->Rlmt.Port[PortNumber].Net->LinksUp > 1) &&
            (pAC->Rlmt.Port[PortNumber].Net->RlmtMode & SK_RLMT_CHECK_SEG) &&
            !(pAC->Rlmt.Port[PortNumber].Net->CheckingState & SK_RLMT_RCS_SEG)) {
            pAC->Rlmt.Port[PortNumber].Net->CheckingState |=
                SK_RLMT_RCS_START_SEG | SK_RLMT_RCS_SEND_SEG;
        }

        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SkRlmtCheckPort: PortsSuspect %d, PcsRx %d.\n",
                pRPort->PortsSuspect, pRPort->CheckingState & SK_RLMT_PCS_RX))

        if (pRPort->PortState != SK_RLMT_PS_DOWN) {
            NewTimeout = TO_SHORTEN(pAC->Rlmt.Port[PortNumber].Net->TimeoutValue);
            if (NewTimeout < SK_RLMT_MIN_TO_VAL) {
                NewTimeout = SK_RLMT_MIN_TO_VAL;
            }

            if (!(pRPort->CheckingState & SK_RLMT_PCS_RX)) {
                Para.Para32[0] = PortNumber;
                pRPort->CheckingState |= SK_RLMT_PCS_RX;

                /*
                 * What shall we do if the port checked by this one receives
                 * our request frames?  What's bad - our rx line or his tx line?
                 */
                Para.Para32[1] = (SK_U32)-1;
                SkTimerStart(pAC, IoC, &pRPort->DownRxTimer,
                    SK_RLMT_PORTDOWN_TIM_VAL, SKGE_RLMT,
                    SK_RLMT_PORTDOWN_RX_TIM, Para);

                for (i = 0; i < pRPort->PortsChecked; i++) {
                    if (pRPort->PortCheck[i].SuspectTx) {
                        continue;
                    }
                    pRPort->PortCheck[i].SuspectTx = SK_TRUE;
                    pRPort->PortsSuspect++;
                    if ((Para.pParaPtr =
                        SkRlmtBuildPacket(pAC, IoC, PortNumber, SK_PACKET_CHECK_TX,
                            &pAC->Addr.Port[PortNumber].CurrentMacAddress,
                            &pRPort->PortCheck[i].CheckAddr)) != NULL) {
                        SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
                    }
                }
            }
        }
        else {    /* PortDown -- or all partners suspect. */
            NewTimeout = SK_RLMT_DEF_TO_VAL;
        }
        pRPort->PortNoRx = SK_TRUE;
    }
    else {    /* A non-BPDU packet was received. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SkRlmtCheckPort %d: %d (%d) receives in last time slot.\n",
                PortNumber,
                pRPort->PacketsPerTimeSlot - pRPort->BpduPacketsPerTimeSlot,
                pRPort->PacketsPerTimeSlot))
        
        SkRlmtPortReceives(pAC, IoC, PortNumber);
        if (pAC->Rlmt.CheckSwitch) {
            SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);
        }

        NewTimeout = SK_RLMT_DEF_TO_VAL;              
    }

    return (NewTimeout);       
}    /* SkRlmtCheckPort */


/******************************************************************************
 *
 *    SkRlmtSelectBcRx - select new active port, criteria 1 (CLP)
 *
 * Description:
 *    This routine selects the port that received a broadcast frame
 *    substantially later than all other ports.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    SK_BOOL
 */
RLMT_STATIC SK_BOOL    SkRlmtSelectBcRx(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    Active,        /* Active port */
SK_U32    PrefPort,    /* Preferred port */
SK_U32    *pSelect)    /* New active port */
{
    SK_U64        BcTimeStamp;
    SK_U32        i;
    SK_BOOL        PortFound;

    BcTimeStamp = 0;    /* Not totally necessary, but feeling better. */
    PortFound = SK_FALSE;
    
    /* Select port with the latest TimeStamp. */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
#ifdef xDEBUG
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("TimeStamp Port %d: %08x %08x.\n",
                i,
                *((SK_U32*)(&pAC->Rlmt.Port[i].BcTimeStamp) + OFFS_HI32),
                *((SK_U32*)(&pAC->Rlmt.Port[i].BcTimeStamp) + OFFS_LO32)))
#endif    /* DEBUG */
        if (!pAC->Rlmt.Port[i].PortDown && !pAC->Rlmt.Port[i].PortNoRx) {
            if (!PortFound || pAC->Rlmt.Port[i].BcTimeStamp > BcTimeStamp) {
                BcTimeStamp = pAC->Rlmt.Port[i].BcTimeStamp;
                *pSelect = i;
                PortFound = SK_TRUE;
            }
        }
    }

    if (PortFound) {
#if 0
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Port %d received the last broadcast.\n", *pSelect))
#endif    /* 0 */

        /* Look if another port's time stamp is similar. */
        for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
            if (i == *pSelect) {
                continue;
            }
            if (!pAC->Rlmt.Port[i].PortDown && !pAC->Rlmt.Port[i].PortNoRx &&
                (pAC->Rlmt.Port[i].BcTimeStamp >
                 BcTimeStamp - SK_RLMT_BC_DELTA ||
                pAC->Rlmt.Port[i].BcTimeStamp +
                 SK_RLMT_BC_DELTA > BcTimeStamp)) {
                PortFound = SK_FALSE;
#ifdef xDEBUG
                SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                    ("Port %d received a broadcast at a similar time.\n", i))
#endif    /* DEBUG */
                break;
            }
        }
    }

#ifdef xDEBUG
    if (PortFound) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_CHECK_SWITCH found Port %d receiving the substantially latest broadcast (%d).\n",
                *pSelect,
                BcTimeStamp - pAC->Rlmt.Port[1 - *pSelect].BcTimeStamp))
    }
#endif    /* DEBUG */

    return (PortFound);
}    /* SkRlmtSelectBcRx */


/******************************************************************************
 *
 *    SkRlmtSelectNotSuspect - select new active port, criteria 2 (CLP)
 *
 * Description:
 *    This routine selects a good port (it is PortUp && !SuspectRx).
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    SK_BOOL
 */
RLMT_STATIC SK_BOOL    SkRlmtSelectNotSuspect(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    Active,        /* Active port */
SK_U32    PrefPort,    /* Preferred port */
SK_U32    *pSelect)    /* New active port */
{
    SK_U32        i;
    SK_BOOL        PortFound;

    PortFound = SK_FALSE;

    /* Select first port that is PortUp && !SuspectRx. */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        if (!pAC->Rlmt.Port[i].PortDown &&
            !(pAC->Rlmt.Port[i].CheckingState & SK_RLMT_PCS_RX)) {
            *pSelect = i;
            if (!pAC->Rlmt.Port[Active].PortDown &&
                !(pAC->Rlmt.Port[Active].CheckingState & SK_RLMT_PCS_RX)) {
                *pSelect = Active;
            }
            if (!pAC->Rlmt.Port[PrefPort].PortDown &&
                !(pAC->Rlmt.Port[PrefPort].CheckingState & SK_RLMT_PCS_RX)) {
                *pSelect = PrefPort;
            }
            PortFound = SK_TRUE;
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("SK_RLMT_CHECK_SWITCH found Port %d up and not check RX.\n",
                    *pSelect))
            break;
        }
    }
    return (PortFound);
}    /* SkRlmtSelectNotSuspect */


/******************************************************************************
 *
 *    SkRlmtSelectUp - select new active port, criteria 3, 4 (CLP)
 *
 * Description:
 *    This routine selects a port that is up.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    SK_BOOL
 */
RLMT_STATIC SK_BOOL    SkRlmtSelectUp(
SK_AC    *pAC,            /* Adapter Context */
SK_IOC    IoC,            /* I/O Context */
SK_U32    Active,            /* Active port */
SK_U32    PrefPort,        /* Preferred port */
SK_U32    *pSelect,        /* New active port */
SK_BOOL    AutoNegDone)    /* Successfully auto-negotiated? */
{
    SK_U32        i;
    SK_BOOL        PortFound;

    PortFound = SK_FALSE;

    /* Select first port that is PortUp. */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_UP &&
            pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) {
            *pSelect = i;
            if (pAC->Rlmt.Port[Active].PortState == SK_RLMT_PS_UP &&
                pAC->GIni.GP[Active].PAutoNegFail != AutoNegDone) {
                *pSelect = Active;
            }
            if (pAC->Rlmt.Port[PrefPort].PortState == SK_RLMT_PS_UP &&
                pAC->GIni.GP[PrefPort].PAutoNegFail != AutoNegDone) {
                *pSelect = PrefPort;
            }
            PortFound = SK_TRUE;
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("SK_RLMT_CHECK_SWITCH found Port %d up.\n", *pSelect))
            break;
        }
    }
    return (PortFound);
}    /* SkRlmtSelectUp */


/******************************************************************************
 *
 *    SkRlmtSelectGoingUp - select new active port, criteria 5, 6 (CLP)
 *
 * Description:
 *    This routine selects the port that is going up for the longest time.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    SK_BOOL
 */
RLMT_STATIC SK_BOOL    SkRlmtSelectGoingUp(
SK_AC    *pAC,            /* Adapter Context */
SK_IOC    IoC,            /* I/O Context */
SK_U32    Active,            /* Active port */
SK_U32    PrefPort,        /* Preferred port */
SK_U32    *pSelect,        /* New active port */
SK_BOOL    AutoNegDone)    /* Successfully auto-negotiated? */
{
    SK_U64        GuTimeStamp;
    SK_U32        i;
    SK_BOOL        PortFound;

    GuTimeStamp = 0;
    PortFound = SK_FALSE;

    /* Select port that is PortGoingUp for the longest time. */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_GOING_UP &&
            pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) {
            GuTimeStamp = pAC->Rlmt.Port[i].GuTimeStamp;
            *pSelect = i;
            PortFound = SK_TRUE;
            break;
        }
    }

    if (!PortFound) {
        return (SK_FALSE);
    }

    for (i = *pSelect + 1; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_GOING_UP &&
            pAC->Rlmt.Port[i].GuTimeStamp < GuTimeStamp &&
            pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) {
            GuTimeStamp = pAC->Rlmt.Port[i].GuTimeStamp;
            *pSelect = i;
        }
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_CHECK_SWITCH found Port %d going up.\n", *pSelect))
    return (SK_TRUE);
}    /* SkRlmtSelectGoingUp */


/******************************************************************************
 *
 *    SkRlmtSelectDown - select new active port, criteria 7, 8 (CLP)
 *
 * Description:
 *    This routine selects a port that is down.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    SK_BOOL
 */
RLMT_STATIC SK_BOOL    SkRlmtSelectDown(
SK_AC    *pAC,            /* Adapter Context */
SK_IOC    IoC,            /* I/O Context */
SK_U32    Active,            /* Active port */
SK_U32    PrefPort,        /* Preferred port */
SK_U32    *pSelect,        /* New active port */
SK_BOOL    AutoNegDone)    /* Successfully auto-negotiated? */
{
    SK_U32        i;
    SK_BOOL        PortFound;

    PortFound = SK_FALSE;

    /* Select first port that is PortDown. */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        if (pAC->Rlmt.Port[i].PortState == SK_RLMT_PS_DOWN &&
            pAC->GIni.GP[i].PAutoNegFail != AutoNegDone) {
            *pSelect = i;
            if (pAC->Rlmt.Port[Active].PortState == SK_RLMT_PS_DOWN &&
                pAC->GIni.GP[Active].PAutoNegFail != AutoNegDone) {
                *pSelect = Active;
            }
            if (pAC->Rlmt.Port[PrefPort].PortState == SK_RLMT_PS_DOWN &&
                pAC->GIni.GP[PrefPort].PAutoNegFail != AutoNegDone) {
                *pSelect = PrefPort;
            }
            PortFound = SK_TRUE;
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("SK_RLMT_CHECK_SWITCH found Port %d down.\n", *pSelect))
            break;
        }
    }
    return (PortFound);
}    /* SkRlmtSelectDown */


/******************************************************************************
 *
 *    SkRlmtCheckSwitch - select new active port and switch to it
 *
 * Description:
 *    This routine decides which port should be the active one and queues
 *    port switching if necessary.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing.
 */
RLMT_STATIC void    SkRlmtCheckSwitch(
SK_AC    *pAC,    /* Adapter Context */
SK_IOC    IoC,    /* I/O Context */
SK_U32    NetIdx)    /* Net index */
{
    SK_EVPARA    Para;
    SK_U32        Active;
    SK_U32        PrefPort;
    SK_U32        i;
    SK_BOOL        PortFound;

    Active = pAC->Rlmt.Net[NetIdx].ActivePort;    /* Index of active port. */
    PrefPort = pAC->Rlmt.Net[NetIdx].PrefPort;    /* Index of preferred port. */
    PortFound = SK_FALSE;
    pAC->Rlmt.CheckSwitch = SK_FALSE;

    if (pAC->Rlmt.Net[NetIdx].LinksUp == 0) {
        /* Last link went down - shut down the net. */
        pAC->Rlmt.Net[NetIdx].RlmtState = SK_RLMT_RS_NET_DOWN;
        Para.Para32[0] = SK_RLMT_NET_DOWN_TEMP;
        Para.Para32[1] = NetIdx;
        SkEventQueue(pAC, SKGE_DRV, SK_DRV_NET_DOWN, Para);

        Para.Para32[0] = pAC->Rlmt.Net[NetIdx].
            Port[pAC->Rlmt.Net[NetIdx].ActivePort]->PortNumber;
        Para.Para32[1] = NetIdx;
        SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_ACTIVE_DOWN, Para);
        return;
    }    /* pAC->Rlmt.LinksUp == 0 */
    else if (pAC->Rlmt.Net[NetIdx].LinksUp == 1 &&
        pAC->Rlmt.Net[NetIdx].RlmtState == SK_RLMT_RS_NET_DOWN) {
        /* First link came up - get the net up. */
        pAC->Rlmt.Net[NetIdx].RlmtState = SK_RLMT_RS_NET_UP;

        /*
         * If pAC->Rlmt.ActivePort != Para.Para32[0],
         * the DRV switches to the port that came up.
         */
        for (i = 0; i < pAC->Rlmt.Net[NetIdx].NumPorts; i++) {
            if (!pAC->Rlmt.Net[NetIdx].Port[i]->LinkDown) {
                if (!pAC->Rlmt.Net[NetIdx].Port[Active]->LinkDown) {
                    i = Active;
                }
                if (!pAC->Rlmt.Net[NetIdx].Port[PrefPort]->LinkDown) {
                    i = PrefPort;
                }
                PortFound = SK_TRUE;
                break;
            }
        }

        if (PortFound) {
            Para.Para32[0] = pAC->Rlmt.Net[NetIdx].Port[i]->PortNumber;
            Para.Para32[1] = NetIdx;
            SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_ACTIVE_UP, Para);

            pAC->Rlmt.Net[NetIdx].ActivePort = i;
            Para.Para32[0] = pAC->Rlmt.Net[NetIdx].Port[i]->PortNumber;
            Para.Para32[1] = NetIdx;
            SkEventQueue(pAC, SKGE_DRV, SK_DRV_NET_UP, Para);

            if ((pAC->Rlmt.Net[NetIdx].RlmtMode & SK_RLMT_TRANSPARENT) == 0 &&
                (Para.pParaPtr = SkRlmtBuildPacket(pAC, IoC,
                pAC->Rlmt.Net[NetIdx].Port[i]->PortNumber,
                SK_PACKET_ANNOUNCE, &pAC->Addr.Net[NetIdx].
                CurrentMacAddress, &SkRlmtMcAddr)) != NULL) {
                /*
                 * Send announce packet to RLMT multicast address to force
                 * switches to learn the new location of the logical MAC address.
                 */
                SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
            }
        }
        else {
            SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E007, SKERR_RLMT_E007_MSG);
        }

        return;
    }    /* LinksUp == 1 && RlmtState == SK_RLMT_RS_NET_DOWN */
    else {    /* Cannot be reached in dual-net mode. */
        Para.Para32[0] = Active;

        /*
         * Preselection:
         *    If RLMT Mode != CheckLinkState
         *        select port that received a broadcast frame substantially later
         *        than all other ports
         *    else select first port that is not SuspectRx
         *    else select first port that is PortUp
         *    else select port that is PortGoingUp for the longest time
         *    else select first port that is PortDown
         *    else stop.
         *
         * For the preselected port:
         *    If ActivePort is equal in quality, select ActivePort.
         *
         *    If PrefPort is equal in quality, select PrefPort.
         *
         *    If ActivePort != SelectedPort,
         *        If old ActivePort is LinkDown,
         *            SwitchHard
         *        else
         *            SwitchSoft
         */
        if (pAC->Rlmt.Net[0].RlmtMode != SK_RLMT_MODE_CLS) {
            if (!PortFound) {
                PortFound = SkRlmtSelectBcRx(
                    pAC, IoC, Active, PrefPort, &Para.Para32[1]);
            }

            if (!PortFound) {
                PortFound = SkRlmtSelectNotSuspect(
                    pAC, IoC, Active, PrefPort, &Para.Para32[1]);
            }
        }    /* pAC->Rlmt.RlmtMode != SK_RLMT_MODE_CLS */

        if (!PortFound) {
            PortFound = SkRlmtSelectUp(
                pAC, IoC, Active, PrefPort, &Para.Para32[1], AUTONEG_SUCCESS);
        }

        if (!PortFound) {
            PortFound = SkRlmtSelectUp(
                pAC, IoC, Active, PrefPort, &Para.Para32[1], AUTONEG_FAILED);
        }

        if (!PortFound) {
            PortFound = SkRlmtSelectGoingUp(
                pAC, IoC, Active, PrefPort, &Para.Para32[1], AUTONEG_SUCCESS);
        }

        if (!PortFound) {
            PortFound = SkRlmtSelectGoingUp(
                pAC, IoC, Active, PrefPort, &Para.Para32[1], AUTONEG_FAILED);
        }

        if (pAC->Rlmt.Net[0].RlmtMode != SK_RLMT_MODE_CLS) {
            if (!PortFound) {
                PortFound = SkRlmtSelectDown(pAC, IoC,
                    Active, PrefPort, &Para.Para32[1], AUTONEG_SUCCESS);
            }

            if (!PortFound) {
                PortFound = SkRlmtSelectDown(pAC, IoC,
                    Active, PrefPort, &Para.Para32[1], AUTONEG_FAILED);
            }
        }    /* pAC->Rlmt.RlmtMode != SK_RLMT_MODE_CLS */

        if (PortFound) {
            if (Para.Para32[1] != Active) {
                SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                    ("Active: %d, Para1: %d.\n", Active, Para.Para32[1]))
                pAC->Rlmt.Net[NetIdx].ActivePort = Para.Para32[1];
                Para.Para32[0] = pAC->Rlmt.Net[NetIdx].
                    Port[Para.Para32[0]]->PortNumber;
                Para.Para32[1] = pAC->Rlmt.Net[NetIdx].
                    Port[Para.Para32[1]]->PortNumber;
                SK_HWAC_LINK_LED(pAC, IoC, Para.Para32[1], SK_LED_ACTIVE);
                if (pAC->Rlmt.Port[Active].LinkDown) {
                    SkEventQueue(pAC, SKGE_DRV, SK_DRV_SWITCH_HARD, Para);
                }
                else {
                    SK_HWAC_LINK_LED(pAC, IoC, Para.Para32[0], SK_LED_STANDBY);
                    SkEventQueue(pAC, SKGE_DRV, SK_DRV_SWITCH_SOFT, Para);
                }
                Para.Para32[1] = NetIdx;
                Para.Para32[0] =
                    pAC->Rlmt.Net[NetIdx].Port[Para.Para32[0]]->PortNumber;
                SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_ACTIVE_DOWN, Para);
                Para.Para32[0] = pAC->Rlmt.Net[NetIdx].
                    Port[pAC->Rlmt.Net[NetIdx].ActivePort]->PortNumber;
                SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_ACTIVE_UP, Para);
                if ((pAC->Rlmt.Net[NetIdx].RlmtMode & SK_RLMT_TRANSPARENT) == 0 &&
                    (Para.pParaPtr = SkRlmtBuildPacket(pAC, IoC, Para.Para32[0],
                    SK_PACKET_ANNOUNCE, &pAC->Addr.Net[NetIdx].CurrentMacAddress,
                    &SkRlmtMcAddr)) != NULL) {
                    /*
                     * Send announce packet to RLMT multicast address to force
                     * switches to learn the new location of the logical
                     * MAC address.
                     */
                    SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para);
                }    /* (Para.pParaPtr = SkRlmtBuildPacket(...)) != NULL */
            }    /* Para.Para32[1] != Active */
        }    /* PortFound */
        else {
            SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E004, SKERR_RLMT_E004_MSG);
        }
    }    /* LinksUp > 1 || LinksUp == 1 && RlmtState != SK_RLMT_RS_NET_DOWN */
    return;
}    /* SkRlmtCheckSwitch */


/******************************************************************************
 *
 *    SkRlmtCheckSeg - Report if segmentation is detected
 *
 * Description:
 *    This routine checks if the ports see different root bridges and reports
 *    segmentation in such a case.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing.
 */
RLMT_STATIC void    SkRlmtCheckSeg(
SK_AC    *pAC,    /* Adapter Context */
SK_IOC    IoC,    /* I/O Context */
SK_U32    NetIdx)    /* Net number */
{
    SK_EVPARA    Para;
    SK_RLMT_NET    *pNet;
    SK_U32        i, j;
    SK_BOOL        Equal;

    pNet = &pAC->Rlmt.Net[NetIdx];
    pNet->RootIdSet = SK_FALSE;
    Equal = SK_TRUE;

    for (i = 0; i < pNet->NumPorts; i++) {
        if (pNet->Port[i]->LinkDown || !pNet->Port[i]->RootIdSet) {
            continue;
        }

        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_DUMP,
            ("Root ID %d: %02x %02x %02x %02x %02x %02x %02x %02x.\n", i,
                pNet->Port[i]->Root.Id[0], pNet->Port[i]->Root.Id[1],
                pNet->Port[i]->Root.Id[2], pNet->Port[i]->Root.Id[3],
                pNet->Port[i]->Root.Id[4], pNet->Port[i]->Root.Id[5],
                pNet->Port[i]->Root.Id[6], pNet->Port[i]->Root.Id[7]))

        if (!pNet->RootIdSet) {
            pNet->Root = pNet->Port[i]->Root;
            pNet->RootIdSet = SK_TRUE;
            continue;
        }

        for (j = 0; j < 8; j ++) {
            Equal &= pNet->Port[i]->Root.Id[j] == pNet->Root.Id[j];
            if (!Equal) {
                break;
            }
        }
        
        if (!Equal) {
            SK_ERR_LOG(pAC, SK_ERRCL_COMM, SKERR_RLMT_E005, SKERR_RLMT_E005_MSG);
            Para.Para32[0] = NetIdx;
            Para.Para32[1] = (SK_U32)-1;
            SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_SEGMENTATION, Para);

            pNet->CheckingState &= ~SK_RLMT_RCS_REPORT_SEG;

            /* 2000-03-06 RA: New. */
            Para.Para32[0] = NetIdx;
            Para.Para32[1] = (SK_U32)-1;
            SkTimerStart(pAC, IoC, &pNet->SegTimer, SK_RLMT_SEG_TO_VAL,
                SKGE_RLMT, SK_RLMT_SEG_TIM, Para);
            break;
        }
    }    /* for (i = 0; i < pNet->NumPorts; i++) */

    /* 2000-03-06 RA: Moved here. */
    /* Segmentation check not running anymore. */
    pNet->CheckingState &= ~SK_RLMT_RCS_SEG;

}    /* SkRlmtCheckSeg */


/******************************************************************************
 *
 *    SkRlmtPortStart - initialize port variables and start port
 *
 * Description:
 *    This routine initializes a port's variables and issues a PORT_START
 *    to the HWAC module.  This handles retries if the start fails or the
 *    link eventually goes down.
 *
 * Context:
 *    runtime, pageable?
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtPortStart(
SK_AC    *pAC,        /* Adapter Context */
SK_IOC    IoC,        /* I/O Context */
SK_U32    PortNumber)    /* Port number */
{
    SK_EVPARA    Para;

    pAC->Rlmt.Port[PortNumber].PortState = SK_RLMT_PS_LINK_DOWN;
    pAC->Rlmt.Port[PortNumber].PortStarted = SK_TRUE;
    pAC->Rlmt.Port[PortNumber].LinkDown = SK_TRUE;
    pAC->Rlmt.Port[PortNumber].PortDown = SK_TRUE;
    pAC->Rlmt.Port[PortNumber].CheckingState = 0;
    pAC->Rlmt.Port[PortNumber].RootIdSet = SK_FALSE;
    Para.Para32[0] = PortNumber;
    Para.Para32[1] = (SK_U32)-1;
    SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_START, Para);
}    /* SkRlmtPortStart */


/******************************************************************************
 *
 *    SkRlmtEvtPortStartTim - PORT_START_TIM
 *
 * Description:
 *    This routine handles PORT_START_TIM events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPortStartTim(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 -1 */
{
    SK_U32            i;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTSTART_TIMEOUT Port %d Event BEGIN.\n", Para.Para32[0]))

        if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORTSTART_TIMEOUT Event EMPTY.\n"))
        return;
    }

    /*
     * Used to start non-preferred ports if the preferred one
     * does not come up.
     * This timeout needs only be set when starting the first
     * (preferred) port.
     */
    if (pAC->Rlmt.Port[Para.Para32[0]].LinkDown) {
        /* PORT_START failed. */
        for (i = 0; i < pAC->Rlmt.Port[Para.Para32[0]].Net->NumPorts; i++) {
            if (!pAC->Rlmt.Port[Para.Para32[0]].Net->Port[i]->PortStarted) {
                SkRlmtPortStart(pAC, IoC,
                    pAC->Rlmt.Port[Para.Para32[0]].Net->Port[i]->PortNumber);
            }
        }
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTSTART_TIMEOUT Event END.\n"))
}    /* SkRlmtEvtPortStartTim */


/******************************************************************************
 *
 *    SkRlmtEvtLinkUp - LINK_UP
 *
 * Description:
 *    This routine handles LLINK_UP events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtLinkUp(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 Undefined */
{
    SK_U32            i;
    SK_RLMT_PORT    *pRPort;
    SK_EVPARA        Para2;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_LINK_UP Port %d Event BEGIN.\n", Para.Para32[0]))

    pRPort = &pAC->Rlmt.Port[Para.Para32[0]];
    if (!pRPort->PortStarted) {
        SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E008, SKERR_RLMT_E008_MSG);

        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("SK_RLMT_LINK_UP Event EMPTY.\n"))
        return;
    }

    if (!pRPort->LinkDown) {
        /* RA;:;: Any better solution? */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_LINK_UP Event EMPTY.\n"))
        return;
    }

    SkTimerStop(pAC, IoC, &pRPort->UpTimer);
    SkTimerStop(pAC, IoC, &pRPort->DownRxTimer);
    SkTimerStop(pAC, IoC, &pRPort->DownTxTimer);

    /* Do something if timer already fired? */

    pRPort->LinkDown = SK_FALSE;
    pRPort->PortState = SK_RLMT_PS_GOING_UP;
    pRPort->GuTimeStamp = SkOsGetTime(pAC);
    pRPort->BcTimeStamp = 0;
    pRPort->Net->LinksUp++;
    if (pRPort->Net->LinksUp == 1) {
        SK_HWAC_LINK_LED(pAC, IoC, Para.Para32[0], SK_LED_ACTIVE);
    }
    else {
        SK_HWAC_LINK_LED(pAC, IoC, Para.Para32[0], SK_LED_STANDBY);
    }

    for (i = 0; i < pRPort->Net->NumPorts; i++) {
        if (!pRPort->Net->Port[i]->PortStarted) {
            SkRlmtPortStart(pAC, IoC, pRPort->Net->Port[i]->PortNumber);
        }
    }

    SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);

    if (pRPort->Net->LinksUp >= 2) {
        if (pRPort->Net->RlmtMode & SK_RLMT_CHECK_LOC_LINK) {
            /* Build the check chain. */
            SkRlmtBuildCheckChain(pAC, pRPort->Net->NetNumber);
        }
    }

    /* If the first link comes up, start the periodical RLMT timeout. */
    if (pRPort->Net->NumPorts > 1 && pRPort->Net->LinksUp == 1 &&
        (pRPort->Net->RlmtMode & SK_RLMT_CHECK_OTHERS) != 0) {
        Para2.Para32[0] = pRPort->Net->NetNumber;
        Para2.Para32[1] = (SK_U32)-1;
        SkTimerStart(pAC, IoC, &pRPort->Net->LocTimer,
            pRPort->Net->TimeoutValue, SKGE_RLMT, SK_RLMT_TIM, Para2);
    }

    Para2 = Para;
    Para2.Para32[1] = (SK_U32)-1;
    SkTimerStart(pAC, IoC, &pRPort->UpTimer, SK_RLMT_PORTUP_TIM_VAL,
        SKGE_RLMT, SK_RLMT_PORTUP_TIM, Para2);
    
    /* Later: if (pAC->Rlmt.RlmtMode & SK_RLMT_CHECK_LOC_LINK) && */
    if ((pRPort->Net->RlmtMode & SK_RLMT_TRANSPARENT) == 0 &&
        (pRPort->Net->RlmtMode & SK_RLMT_CHECK_LINK) != 0 &&
        (Para2.pParaPtr =
            SkRlmtBuildPacket(pAC, IoC, Para.Para32[0], SK_PACKET_ANNOUNCE,
            &pAC->Addr.Port[Para.Para32[0]].CurrentMacAddress, &SkRlmtMcAddr)
        ) != NULL) {
        /* Send "new" packet to RLMT multicast address. */
        SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para2);
    }

    if (pRPort->Net->RlmtMode & SK_RLMT_CHECK_SEG) {
        if ((Para2.pParaPtr =
            SkRlmtBuildSpanningTreePacket(pAC, IoC, Para.Para32[0])) != NULL) {
            pAC->Rlmt.Port[Para.Para32[0]].RootIdSet = SK_FALSE;
            pRPort->Net->CheckingState |=
                SK_RLMT_RCS_SEG | SK_RLMT_RCS_REPORT_SEG;

            SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para2);

            Para.Para32[1] = (SK_U32)-1;
            SkTimerStart(pAC, IoC, &pRPort->Net->SegTimer,
                SK_RLMT_SEG_TO_VAL, SKGE_RLMT, SK_RLMT_SEG_TIM, Para);
        }
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_LINK_UP Event END.\n"))
}    /* SkRlmtEvtLinkUp */


/******************************************************************************
 *
 *    SkRlmtEvtPortUpTim - PORT_UP_TIM
 *
 * Description:
 *    This routine handles PORT_UP_TIM events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPortUpTim(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 -1 */
{
    SK_RLMT_PORT    *pRPort;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTUP_TIM Port %d Event BEGIN.\n", Para.Para32[0]))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORTUP_TIM Event EMPTY.\n"))
        return;
    }

    pRPort = &pAC->Rlmt.Port[Para.Para32[0]];
    if (pRPort->LinkDown || (pRPort->PortState == SK_RLMT_PS_UP)) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORTUP_TIM Port %d Event EMPTY.\n", Para.Para32[0]))
        return;
    }

    pRPort->PortDown = SK_FALSE;
    pRPort->PortState = SK_RLMT_PS_UP;
    pRPort->Net->PortsUp++;
    if (pRPort->Net->RlmtState != SK_RLMT_RS_INIT) {
        if (pAC->Rlmt.NumNets <= 1) {
            SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);
        }
        SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_PORT_UP, Para);
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTUP_TIM Event END.\n"))
}    /* SkRlmtEvtPortUpTim */


/******************************************************************************
 *
 *    SkRlmtEvtPortDownTim - PORT_DOWN_*
 *
 * Description:
 *    This routine handles PORT_DOWN_* events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPortDownX(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_U32        Event,    /* Event code */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 -1 */
{
    SK_RLMT_PORT    *pRPort;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTDOWN* Port %d Event (%d) BEGIN.\n",
            Para.Para32[0], Event))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORTDOWN* Event EMPTY.\n"))
        return;
    }

    pRPort = &pAC->Rlmt.Port[Para.Para32[0]];
    if (!pRPort->PortStarted || (Event == SK_RLMT_PORTDOWN_TX_TIM &&
        !(pRPort->CheckingState & SK_RLMT_PCS_TX))) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORTDOWN* Event (%d) EMPTY.\n", Event))
        return;
    }
    
    /* Stop port's timers. */
    SkTimerStop(pAC, IoC, &pRPort->UpTimer);
    SkTimerStop(pAC, IoC, &pRPort->DownRxTimer);
    SkTimerStop(pAC, IoC, &pRPort->DownTxTimer);

    if (pRPort->PortState != SK_RLMT_PS_LINK_DOWN) {
        pRPort->PortState = SK_RLMT_PS_DOWN;
    }

    if (!pRPort->PortDown) {
        pRPort->Net->PortsUp--;
        pRPort->PortDown = SK_TRUE;
        SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_PORT_DOWN, Para);
    }

    pRPort->PacketsPerTimeSlot = 0;
    /* pRPort->DataPacketsPerTimeSlot = 0; */
    pRPort->BpduPacketsPerTimeSlot = 0;

    /*
     * RA;:;: To be checked:
     * - actions at RLMT_STOP: We should not switch anymore.
     */
    if (pRPort->Net->RlmtState != SK_RLMT_RS_INIT) {
        if (Para.Para32[0] ==
            pRPort->Net->Port[pRPort->Net->ActivePort]->PortNumber) {
            /* Active Port went down. */
            SkRlmtCheckSwitch(pAC, IoC, pRPort->Net->NetNumber);
        }
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORTDOWN* Event (%d) END.\n", Event))
}    /* SkRlmtEvtPortDownX */


/******************************************************************************
 *
 *    SkRlmtEvtLinkDown - LINK_DOWN
 *
 * Description:
 *    This routine handles LINK_DOWN events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtLinkDown(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 Undefined */
{
    SK_RLMT_PORT    *pRPort;

    pRPort = &pAC->Rlmt.Port[Para.Para32[0]];
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_LINK_DOWN Port %d Event BEGIN.\n", Para.Para32[0]))

    if (!pAC->Rlmt.Port[Para.Para32[0]].LinkDown) {
        pRPort->Net->LinksUp--;
        pRPort->LinkDown = SK_TRUE;
        pRPort->PortState = SK_RLMT_PS_LINK_DOWN;
        SK_HWAC_LINK_LED(pAC, IoC, Para.Para32[0], SK_LED_OFF);

        if ((pRPort->Net->RlmtMode & SK_RLMT_CHECK_LOC_LINK) != 0) {
            /* Build the check chain. */
            SkRlmtBuildCheckChain(pAC, pRPort->Net->NetNumber);
        }

        /* Ensure that port is marked down. */
        Para.Para32[1] = -1;
        (void)SkRlmtEvent(pAC, IoC, SK_RLMT_PORTDOWN, Para);
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_LINK_DOWN Event END.\n"))
}    /* SkRlmtEvtLinkDown */


/******************************************************************************
 *
 *    SkRlmtEvtPortAddr - PORT_ADDR
 *
 * Description:
 *    This routine handles PORT_ADDR events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPortAddr(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortNumber; SK_U32 -1 */
{
    SK_U32            i, j;
    SK_RLMT_PORT    *pRPort;
    SK_MAC_ADDR        *pOldMacAddr;
    SK_MAC_ADDR        *pNewMacAddr;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PORT_ADDR Port %d Event BEGIN.\n", Para.Para32[0]))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORT_ADDR Event EMPTY.\n"))
        return;
    }

    /* Port's physical MAC address changed. */
    pOldMacAddr = &pAC->Addr.Port[Para.Para32[0]].PreviousMacAddress;
    pNewMacAddr = &pAC->Addr.Port[Para.Para32[0]].CurrentMacAddress;

    /*
     * NOTE: This is not scalable for solutions where ports are
     *     checked remotely.  There, we need to send an RLMT
     *     address change packet - and how do we ensure delivery?
     */
    for (i = 0; i < (SK_U32)pAC->GIni.GIMacsFound; i++) {
        pRPort = &pAC->Rlmt.Port[i];
        for (j = 0; j < pRPort->PortsChecked; j++) {
            if (SK_ADDR_EQUAL(
                pRPort->PortCheck[j].CheckAddr.a, pOldMacAddr->a)) {
                pRPort->PortCheck[j].CheckAddr = *pNewMacAddr;
            }
        }
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PORT_ADDR Event END.\n"))
}    /* SkRlmtEvtPortAddr */


/******************************************************************************
 *
 *    SkRlmtEvtStart - START
 *
 * Description:
 *    This routine handles START events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtStart(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
    SK_EVPARA    Para2;
    SK_U32        PortIdx;
    SK_U32        PortNumber;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_START Net %d Event BEGIN.\n", Para.Para32[0]))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_START Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[0]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_START Event EMPTY.\n"))
        return;
    }

    if (pAC->Rlmt.Net[Para.Para32[0]].RlmtState != SK_RLMT_RS_INIT) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_START Event EMPTY.\n"))
        return;
    }

    if (pAC->Rlmt.NetsStarted >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("All nets should have been started.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_START Event EMPTY.\n"))
        return;
    }

    if (pAC->Rlmt.Net[Para.Para32[0]].PrefPort >=
        pAC->Rlmt.Net[Para.Para32[0]].NumPorts) {
        SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E009, SKERR_RLMT_E009_MSG);

        /* Change PrefPort to internal default. */
        Para2.Para32[0] = 0xFFFFFFFF;
        Para2.Para32[1] = Para.Para32[0];
        (void)SkRlmtEvent(pAC, IoC, SK_RLMT_PREFPORT_CHANGE, Para2);
    }

    PortIdx = pAC->Rlmt.Net[Para.Para32[0]].PrefPort;
    PortNumber = pAC->Rlmt.Net[Para.Para32[0]].Port[PortIdx]->PortNumber;

    pAC->Rlmt.Net[Para.Para32[0]].LinksUp = 0;
    pAC->Rlmt.Net[Para.Para32[0]].PortsUp = 0;
    pAC->Rlmt.Net[Para.Para32[0]].CheckingState = 0;
    pAC->Rlmt.Net[Para.Para32[0]].RlmtState = SK_RLMT_RS_NET_DOWN;

    /* Start preferred port. */
    SkRlmtPortStart(pAC, IoC, PortNumber);

    /* Start Timer (for first port only). */
    Para2.Para32[0] = PortNumber;
    Para2.Para32[1] = (SK_U32)-1;
    SkTimerStart(pAC, IoC, &pAC->Rlmt.Port[PortNumber].UpTimer,
        SK_RLMT_PORTSTART_TIM_VAL, SKGE_RLMT, SK_RLMT_PORTSTART_TIM, Para2);

    pAC->Rlmt.NetsStarted++;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_START Event END.\n"))
}    /* SkRlmtEvtStart */


/******************************************************************************
 *
 *    SkRlmtEvtStop - STOP
 *
 * Description:
 *    This routine handles STOP events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtStop(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
    SK_EVPARA    Para2;
    SK_U32        PortNumber;
    SK_U32        i;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STOP Net %d Event BEGIN.\n", Para.Para32[0]))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STOP Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[0]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STOP Event EMPTY.\n"))
        return;
    }

    if (pAC->Rlmt.Net[Para.Para32[0]].RlmtState == SK_RLMT_RS_INIT) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STOP Event EMPTY.\n"))
        return;
    }

    if (pAC->Rlmt.NetsStarted == 0) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("All nets are stopped.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STOP Event EMPTY.\n"))
        return;
    }

    /* Stop RLMT timers. */
    SkTimerStop(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[0]].LocTimer); 
    SkTimerStop(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[0]].SegTimer);

    /* Stop net. */
    pAC->Rlmt.Net[Para.Para32[0]].RlmtState = SK_RLMT_RS_INIT;
    pAC->Rlmt.Net[Para.Para32[0]].RootIdSet = SK_FALSE;
    Para2.Para32[0] = SK_RLMT_NET_DOWN_FINAL;
    Para2.Para32[1] = Para.Para32[0];            /* Net# */
    SkEventQueue(pAC, SKGE_DRV, SK_DRV_NET_DOWN, Para2);

    /* Stop ports. */
    for (i = 0; i < pAC->Rlmt.Net[Para.Para32[0]].NumPorts; i++) {
        PortNumber = pAC->Rlmt.Net[Para.Para32[0]].Port[i]->PortNumber;
        if (pAC->Rlmt.Port[PortNumber].PortState != SK_RLMT_PS_INIT) {
            SkTimerStop(pAC, IoC, &pAC->Rlmt.Port[PortNumber].UpTimer);
            SkTimerStop(pAC, IoC, &pAC->Rlmt.Port[PortNumber].DownRxTimer);
            SkTimerStop(pAC, IoC, &pAC->Rlmt.Port[PortNumber].DownTxTimer);

            pAC->Rlmt.Port[PortNumber].PortState = SK_RLMT_PS_INIT;
            pAC->Rlmt.Port[PortNumber].RootIdSet = SK_FALSE;
            pAC->Rlmt.Port[PortNumber].PortStarted = SK_FALSE;
            Para2.Para32[0] = PortNumber;
            Para2.Para32[1] = (SK_U32)-1;
            SkEventQueue(pAC, SKGE_HWAC, SK_HWEV_PORT_STOP, Para2);
        }
    }

    pAC->Rlmt.NetsStarted--;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STOP Event END.\n"))
}    /* SkRlmtEvtStop */


/******************************************************************************
 *
 *    SkRlmtEvtTim - TIM
 *
 * Description:
 *    This routine handles TIM events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtTim(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
    SK_RLMT_PORT    *pRPort;
    SK_U32            Timeout;
    SK_U32            NewTimeout;
    SK_U32            PortNumber;
    SK_U32            i;

#if 0
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_TIM Event BEGIN.\n"))
#endif    /* 0 */

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_TIM Event EMPTY.\n"))
        return;
    }

    if ((pAC->Rlmt.Net[Para.Para32[0]].RlmtMode & SK_RLMT_CHECK_OTHERS) == 0 ||
        pAC->Rlmt.Net[Para.Para32[0]].LinksUp == 0) {
        /* Mode changed or all links down: No more link checking. */
        return;
    }

#if 0
    pAC->Rlmt.SwitchCheckCounter--;
    if (pAC->Rlmt.SwitchCheckCounter == 0) {
        pAC->Rlmt.SwitchCheckCounter;
    }
#endif    /* 0 */

    NewTimeout = SK_RLMT_DEF_TO_VAL;
    for (i = 0; i < pAC->Rlmt.Net[Para.Para32[0]].NumPorts; i++) {
        PortNumber = pAC->Rlmt.Net[Para.Para32[0]].Port[i]->PortNumber;
        pRPort = &pAC->Rlmt.Port[PortNumber];
        if (!pRPort->LinkDown) {
            Timeout = SkRlmtCheckPort(pAC, IoC, PortNumber);
            if (Timeout < NewTimeout) {
                NewTimeout = Timeout;
            }

            /*
             * These counters should be set to 0 for all ports before the
             * first frame is sent in the next loop.
             */
            pRPort->PacketsPerTimeSlot = 0;
            /* pRPort->DataPacketsPerTimeSlot = 0; */
            pRPort->BpduPacketsPerTimeSlot = 0;
        }
    }
    pAC->Rlmt.Net[Para.Para32[0]].TimeoutValue = NewTimeout;

    if (pAC->Rlmt.Net[Para.Para32[0]].LinksUp > 1) {
        /*
         * If checking remote ports, also send packets if
         *   (LinksUp == 1) &&
         *   this port checks at least one (remote) port.
         */

        /*
         * Must be new loop, as SkRlmtCheckPort can request to
         * check segmentation when e.g. checking the last port.
         */
        for (i = 0; i < pAC->Rlmt.Net[Para.Para32[0]].NumPorts; i++) {
            if (!pAC->Rlmt.Net[Para.Para32[0]].Port[i]->LinkDown) {
                SkRlmtSend(pAC, IoC,
                    pAC->Rlmt.Net[Para.Para32[0]].Port[i]->PortNumber);
            }
        }
    }

    SkTimerStart(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[0]].LocTimer,
        pAC->Rlmt.Net[Para.Para32[0]].TimeoutValue, SKGE_RLMT, SK_RLMT_TIM,
        Para);

    if (pAC->Rlmt.Net[Para.Para32[0]].LinksUp > 1 &&
        (pAC->Rlmt.Net[Para.Para32[0]].RlmtMode & SK_RLMT_CHECK_SEG) &&
        (pAC->Rlmt.Net[Para.Para32[0]].CheckingState & SK_RLMT_RCS_START_SEG)) {
        SkTimerStart(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[0]].SegTimer,
            SK_RLMT_SEG_TO_VAL, SKGE_RLMT, SK_RLMT_SEG_TIM, Para);
        pAC->Rlmt.Net[Para.Para32[0]].CheckingState &= ~SK_RLMT_RCS_START_SEG;
        pAC->Rlmt.Net[Para.Para32[0]].CheckingState |=
            SK_RLMT_RCS_SEG | SK_RLMT_RCS_REPORT_SEG;
    }

#if 0
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_TIM Event END.\n"))
#endif    /* 0 */
}    /* SkRlmtEvtTim */


/******************************************************************************
 *
 *    SkRlmtEvtSegTim - SEG_TIM
 *
 * Description:
 *    This routine handles SEG_TIM events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtSegTim(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
#ifdef XDEBUG
    int j;
#endif    /* DEBUG */

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_SEG_TIM Event BEGIN.\n"))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SEG_TIM Event EMPTY.\n"))
        return;
    }

#ifdef xDEBUG
    for (j = 0; i < pAC->Rlmt.Net[Para.Para32[0]].NumPorts; j++) {
        SK_ADDR_PORT    *pAPort;
        SK_U32            k;
        SK_U16            *InAddr;
        SK_U8            InAddr8[6];

        InAddr = (SK_U16 *)&InAddr8[0];
        pAPort = pAC->Rlmt.Net[Para.Para32[0]].Port[j]->AddrPort;
        for (k = 0; k < pAPort->NextExactMatchRlmt; k++) {
            /* Get exact match address k from port j. */
            XM_INADDR(IoC, pAC->Rlmt.Net[Para.Para32[0]].Port[j]->PortNumber,
                XM_EXM(k), InAddr);
            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("MC address %d on Port %u: %02x %02x %02x %02x %02x %02x --  %02x %02x %02x %02x %02x %02x.\n",
                    k, pAC->Rlmt.Net[Para.Para32[0]].Port[j]->PortNumber,
                    InAddr8[0], InAddr8[1], InAddr8[2],
                    InAddr8[3], InAddr8[4], InAddr8[5],
                    pAPort->Exact[k].a[0], pAPort->Exact[k].a[1],
                    pAPort->Exact[k].a[2], pAPort->Exact[k].a[3],
                    pAPort->Exact[k].a[4], pAPort->Exact[k].a[5]))
        }
    }
#endif    /* DEBUG */
                   
    SkRlmtCheckSeg(pAC, IoC, Para.Para32[0]);

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SEG_TIM Event END.\n"))
}    /* SkRlmtEvtSegTim */


/******************************************************************************
 *
 *    SkRlmtEvtPacketRx - PACKET_RECEIVED
 *
 * Description:
 *    This routine handles PACKET_RECEIVED events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPacketRx(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_MBUF *pMb */
{
    SK_MBUF    *pMb;
    SK_MBUF    *pNextMb;
    SK_U32    NetNumber;

#if 0
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PACKET_RECEIVED Event BEGIN.\n"))
#endif    /* 0 */

    /* Should we ignore frames during port switching? */

#ifdef DEBUG
    pMb = Para.pParaPtr;
    if (pMb == NULL) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL, ("No mbuf.\n"))
    }
    else if (pMb->pNext != NULL) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("More than one mbuf or pMb->pNext not set.\n"))
    }
#endif    /* DEBUG */

    for (pMb = Para.pParaPtr; pMb != NULL; pMb = pNextMb) {
        pNextMb = pMb->pNext;
        pMb->pNext = NULL;

        NetNumber = pAC->Rlmt.Port[pMb->PortIdx].Net->NetNumber;
        if (pAC->Rlmt.Net[NetNumber].RlmtState == SK_RLMT_RS_INIT) {
            SkDrvFreeRlmtMbuf(pAC, IoC, pMb);
        }
        else {
            SkRlmtPacketReceive(pAC, IoC, pMb);
        }
    }

#if 0
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PACKET_RECEIVED Event END.\n"))
#endif    /* 0 */
}    /* SkRlmtEvtPacketRx */


/******************************************************************************
 *
 *    SkRlmtEvtStatsClear - STATS_CLEAR
 *
 * Description:
 *    This routine handles STATS_CLEAR events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtStatsClear(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
    SK_U32            i;
    SK_RLMT_PORT    *pRPort;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STATS_CLEAR Event BEGIN.\n"))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STATS_CLEAR Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[0]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STATS_CLEAR Event EMPTY.\n"))
        return;
    }

    /* Clear statistics for logical and physical ports. */
    for (i = 0; i < pAC->Rlmt.Net[Para.Para32[0]].NumPorts; i++) {
        pRPort =
            &pAC->Rlmt.Port[pAC->Rlmt.Net[Para.Para32[0]].Port[i]->PortNumber];
        pRPort->TxHelloCts = 0;
        pRPort->RxHelloCts = 0;
        pRPort->TxSpHelloReqCts = 0;
        pRPort->RxSpHelloCts = 0;
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STATS_CLEAR Event END.\n"))
}    /* SkRlmtEvtStatsClear */


/******************************************************************************
 *
 *    SkRlmtEvtStatsUpdate - STATS_UPDATE
 *
 * Description:
 *    This routine handles STATS_UPDATE events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtStatsUpdate(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NetNumber; SK_U32 -1 */
{
    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STATS_UPDATE Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[0]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_STATS_UPDATE Event EMPTY.\n"))
        return;
    }

#if 0
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STATS_UPDATE Event BEGIN.\n"))

    /* Update statistics - currently always up-to-date. */

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_STATS_UPDATE Event END.\n"))
#endif    /* 0 */
}    /* SkRlmtEvtStatsUpdate */


/******************************************************************************
 *
 *    SkRlmtEvtPrefportChange - PREFPORT_CHANGE
 *
 * Description:
 *    This routine handles PREFPORT_CHANGE events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtPrefportChange(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 PortIndex; SK_U32 NetNumber */
{
    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PREFPORT_CHANGE to Port %d Event BEGIN.\n", Para.Para32[0]))

    if (Para.Para32[1] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[1]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_PREFPORT_CHANGE Event EMPTY.\n"))
        return;
    }

    /* 0xFFFFFFFF == auto-mode. */
    if (Para.Para32[0] == 0xFFFFFFFF) {
        pAC->Rlmt.Net[Para.Para32[1]].PrefPort = SK_RLMT_DEF_PREF_PORT;
    }
    else {
        if (Para.Para32[0] >= pAC->Rlmt.Net[Para.Para32[1]].NumPorts) {
            SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E010, SKERR_RLMT_E010_MSG);

            SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
                ("SK_RLMT_PREFPORT_CHANGE Event EMPTY.\n"))
            return;
        }

        pAC->Rlmt.Net[Para.Para32[1]].PrefPort = Para.Para32[0];
    }

    pAC->Rlmt.Net[Para.Para32[1]].Preference = Para.Para32[0];

    if (pAC->Rlmt.Net[Para.Para32[1]].RlmtState != SK_RLMT_RS_INIT) {
        SkRlmtCheckSwitch(pAC, IoC, Para.Para32[1]);
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_PREFPORT_CHANGE Event END.\n"))
}    /* SkRlmtEvtPrefportChange */


/******************************************************************************
 *
 *    SkRlmtEvtSetNets - SET_NETS
 *
 * Description:
 *    This routine handles SET_NETS events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtSetNets(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NumNets; SK_U32 -1 */
{
    int i;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_SET_NETS Event BEGIN.\n"))

    if (Para.Para32[1] != (SK_U32)-1) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad Parameter.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SET_NETS Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] == 0 || Para.Para32[0] > SK_MAX_NETS ||
        Para.Para32[0] > (SK_U32)pAC->GIni.GIMacsFound) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad number of nets: %d.\n", Para.Para32[0]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SET_NETS Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] == pAC->Rlmt.NumNets) {    /* No change. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SET_NETS Event EMPTY.\n"))
        return;
    }

    /* Entering and leaving dual mode only allowed while nets are stopped. */
    if (pAC->Rlmt.NetsStarted > 0) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Changing dual mode only allowed while all nets are stopped.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SET_NETS Event EMPTY.\n"))
        return;
    }

    if (Para.Para32[0] == 1) {
        if (pAC->Rlmt.NumNets > 1) {
            /* Clear logical MAC addr from second net's active port. */
            (void)SkAddrOverride(pAC, IoC, pAC->Rlmt.Net[1].Port[pAC->Addr.
                Net[1].ActivePort]->PortNumber, NULL, SK_ADDR_CLEAR_LOGICAL);
            pAC->Rlmt.Net[1].NumPorts = 0;
        }

        pAC->Rlmt.NumNets = Para.Para32[0];
        for (i = 0; (SK_U32)i < pAC->Rlmt.NumNets; i++) {
            pAC->Rlmt.Net[i].RlmtState = SK_RLMT_RS_INIT;
            pAC->Rlmt.Net[i].RootIdSet = SK_FALSE;
            pAC->Rlmt.Net[i].Preference = 0xFFFFFFFF;      /* "Automatic" */
            pAC->Rlmt.Net[i].PrefPort = SK_RLMT_DEF_PREF_PORT;
            /* Just assuming. */
            pAC->Rlmt.Net[i].ActivePort = pAC->Rlmt.Net[i].PrefPort;
            pAC->Rlmt.Net[i].RlmtMode = SK_RLMT_DEF_MODE;
            pAC->Rlmt.Net[i].TimeoutValue = SK_RLMT_DEF_TO_VAL;
            pAC->Rlmt.Net[i].NetNumber = i;
        }

        pAC->Rlmt.Port[1].Net= &pAC->Rlmt.Net[0];
        pAC->Rlmt.Net[0].NumPorts = pAC->GIni.GIMacsFound;

        SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_SET_NETS, Para);

        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("RLMT: Changed to one net with two ports.\n"))
    }
    else if (Para.Para32[0] == 2) {
        pAC->Rlmt.Port[1].Net= &pAC->Rlmt.Net[1];
        pAC->Rlmt.Net[1].NumPorts = pAC->GIni.GIMacsFound - 1;
        pAC->Rlmt.Net[0].NumPorts =
            pAC->GIni.GIMacsFound - pAC->Rlmt.Net[1].NumPorts;
        
        pAC->Rlmt.NumNets = Para.Para32[0];
        for (i = 0; (SK_U32)i < pAC->Rlmt.NumNets; i++) {
            pAC->Rlmt.Net[i].RlmtState = SK_RLMT_RS_INIT;
            pAC->Rlmt.Net[i].RootIdSet = SK_FALSE;
            pAC->Rlmt.Net[i].Preference = 0xFFFFFFFF;      /* "Automatic" */
            pAC->Rlmt.Net[i].PrefPort = SK_RLMT_DEF_PREF_PORT;
            /* Just assuming. */
            pAC->Rlmt.Net[i].ActivePort = pAC->Rlmt.Net[i].PrefPort;
            pAC->Rlmt.Net[i].RlmtMode = SK_RLMT_DEF_MODE;
            pAC->Rlmt.Net[i].TimeoutValue = SK_RLMT_DEF_TO_VAL;

            pAC->Rlmt.Net[i].NetNumber = i;
        }

        /* Set logical MAC addr on second net's active port. */
        (void)SkAddrOverride(pAC, IoC, pAC->Rlmt.Net[1].Port[pAC->Addr.
            Net[1].ActivePort]->PortNumber, NULL, SK_ADDR_SET_LOGICAL);

        SkEventQueue(pAC, SKGE_PNMI, SK_PNMI_EVT_RLMT_SET_NETS, Para);

        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("RLMT: Changed to two nets with one port each.\n"))
    }
    else {
        /* Not implemented for more than two nets. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SetNets not implemented for more than two nets.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_SET_NETS Event EMPTY.\n"))
        return;
    }

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_SET_NETS Event END.\n"))
}    /* SkRlmtSetNets */


/******************************************************************************
 *
 *    SkRlmtEvtModeChange - MODE_CHANGE
 *
 * Description:
 *    This routine handles MODE_CHANGE events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    Nothing
 */
RLMT_STATIC void    SkRlmtEvtModeChange(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_EVPARA    Para)    /* SK_U32 NewMode; SK_U32 NetNumber */
{
    SK_EVPARA    Para2;
    SK_U32        i;
    SK_U32        PrevRlmtMode;

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
        ("SK_RLMT_MODE_CHANGE Event BEGIN.\n"))

    if (Para.Para32[1] >= pAC->Rlmt.NumNets) {
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Bad NetNumber %d.\n", Para.Para32[1]))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_MODE_CHANGE Event EMPTY.\n"))
        return;
    }

    Para.Para32[0] |= SK_RLMT_CHECK_LINK;

    if (pAC->Rlmt.Net[Para.Para32[1]].NumPorts < 2 &&
        Para.Para32[0] != SK_RLMT_MODE_CLS) {
        pAC->Rlmt.Net[Para.Para32[1]].RlmtMode = SK_RLMT_MODE_CLS;
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Forced RLMT mode to CLS on single port net.\n"))
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_MODE_CHANGE Event EMPTY.\n"))
        return;
    }

    /* Update RLMT mode. */
    PrevRlmtMode = pAC->Rlmt.Net[Para.Para32[1]].RlmtMode;
    pAC->Rlmt.Net[Para.Para32[1]].RlmtMode = Para.Para32[0];

    if ((PrevRlmtMode & SK_RLMT_CHECK_LOC_LINK) !=
        (pAC->Rlmt.Net[Para.Para32[1]].RlmtMode & SK_RLMT_CHECK_LOC_LINK)) {
        /* SK_RLMT_CHECK_LOC_LINK bit changed. */
        if ((PrevRlmtMode & SK_RLMT_CHECK_OTHERS) == 0 &&
            pAC->Rlmt.Net[Para.Para32[1]].NumPorts > 1 &&
            pAC->Rlmt.Net[Para.Para32[1]].PortsUp >= 1) {
            /* 20001207 RA: Was "PortsUp == 1". */
            Para2.Para32[0] = Para.Para32[1];
            Para2.Para32[1] = (SK_U32)-1;
            SkTimerStart(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[1]].LocTimer,
                pAC->Rlmt.Net[Para.Para32[1]].TimeoutValue,
                SKGE_RLMT, SK_RLMT_TIM, Para2);
        }
    }

    if ((PrevRlmtMode & SK_RLMT_CHECK_SEG) !=
        (pAC->Rlmt.Net[Para.Para32[1]].RlmtMode & SK_RLMT_CHECK_SEG)) {
        /* SK_RLMT_CHECK_SEG bit changed. */
        for (i = 0; i < pAC->Rlmt.Net[Para.Para32[1]].NumPorts; i++) {
            (void)SkAddrMcClear(pAC, IoC,
                pAC->Rlmt.Net[Para.Para32[1]].Port[i]->PortNumber,
                SK_ADDR_PERMANENT | SK_MC_SW_ONLY);

            /* Add RLMT MC address. */
            (void)SkAddrMcAdd(pAC, IoC,
                pAC->Rlmt.Net[Para.Para32[1]].Port[i]->PortNumber,
                &SkRlmtMcAddr, SK_ADDR_PERMANENT);

            if ((pAC->Rlmt.Net[Para.Para32[1]].RlmtMode &
                SK_RLMT_CHECK_SEG) != 0) {
                /* Add BPDU MC address. */
                (void)SkAddrMcAdd(pAC, IoC,
                    pAC->Rlmt.Net[Para.Para32[1]].Port[i]->PortNumber,
                    &BridgeMcAddr, SK_ADDR_PERMANENT);

                if (pAC->Rlmt.Net[Para.Para32[1]].RlmtState != SK_RLMT_RS_INIT) {
                    if (!pAC->Rlmt.Net[Para.Para32[1]].Port[i]->LinkDown &&
                        (Para2.pParaPtr = SkRlmtBuildSpanningTreePacket(
                        pAC, IoC, i)) != NULL) {
                        pAC->Rlmt.Net[Para.Para32[1]].Port[i]->RootIdSet =
                            SK_FALSE;
                        SkEventQueue(pAC, SKGE_DRV, SK_DRV_RLMT_SEND, Para2);
                    }
                }
            }
            (void)SkAddrMcUpdate(pAC, IoC,
                pAC->Rlmt.Net[Para.Para32[1]].Port[i]->PortNumber);
        }    /* for ... */

        if ((pAC->Rlmt.Net[Para.Para32[1]].RlmtMode & SK_RLMT_CHECK_SEG) != 0) {
            Para2.Para32[0] = Para.Para32[1];
            Para2.Para32[1] = (SK_U32)-1;
            SkTimerStart(pAC, IoC, &pAC->Rlmt.Net[Para.Para32[1]].SegTimer,
                SK_RLMT_SEG_TO_VAL, SKGE_RLMT, SK_RLMT_SEG_TIM, Para2);
        }
    }    /* SK_RLMT_CHECK_SEG bit changed. */

    SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("SK_RLMT_MODE_CHANGE Event END.\n"))
}    /* SkRlmtEvtModeChange */


/******************************************************************************
 *
 *    SkRlmtEvent - a PORT- or an RLMT-specific event happened
 *
 * Description:
 *    This routine calls subroutines to handle PORT- and RLMT-specific events.
 *
 * Context:
 *    runtime, pageable?
 *    may be called after SK_INIT_IO
 *
 * Returns:
 *    0
 */
int    SkRlmtEvent(
SK_AC        *pAC,    /* Adapter Context */
SK_IOC        IoC,    /* I/O Context */
SK_U32        Event,    /* Event code */
SK_EVPARA    Para)    /* Event-specific parameter */
{
    switch (Event) {
    
    /* ----- PORT events ----- */

    case SK_RLMT_PORTSTART_TIM:    /* From RLMT via TIME. */
        SkRlmtEvtPortStartTim(pAC, IoC, Para);
        break;
    case SK_RLMT_LINK_UP:        /* From SIRQ. */
        SkRlmtEvtLinkUp(pAC, IoC, Para);
        break;
    case SK_RLMT_PORTUP_TIM:    /* From RLMT via TIME. */
        SkRlmtEvtPortUpTim(pAC, IoC, Para);
        break;
    case SK_RLMT_PORTDOWN:            /* From RLMT. */
    case SK_RLMT_PORTDOWN_RX_TIM:    /* From RLMT via TIME. */
    case SK_RLMT_PORTDOWN_TX_TIM:    /* From RLMT via TIME. */
        SkRlmtEvtPortDownX(pAC, IoC, Event, Para);
        break;
    case SK_RLMT_LINK_DOWN:        /* From SIRQ. */
        SkRlmtEvtLinkDown(pAC, IoC, Para);
        break;
    case SK_RLMT_PORT_ADDR:        /* From ADDR. */
        SkRlmtEvtPortAddr(pAC, IoC, Para);
        break;

    /* ----- RLMT events ----- */

    case SK_RLMT_START:        /* From DRV. */
        SkRlmtEvtStart(pAC, IoC, Para);
        break;
    case SK_RLMT_STOP:        /* From DRV. */
        SkRlmtEvtStop(pAC, IoC, Para);
        break;
    case SK_RLMT_TIM:        /* From RLMT via TIME. */
        SkRlmtEvtTim(pAC, IoC, Para);
        break;
    case SK_RLMT_SEG_TIM:
        SkRlmtEvtSegTim(pAC, IoC, Para);
        break;
    case SK_RLMT_PACKET_RECEIVED:    /* From DRV. */
        SkRlmtEvtPacketRx(pAC, IoC, Para);
        break;
    case SK_RLMT_STATS_CLEAR:    /* From PNMI. */
        SkRlmtEvtStatsClear(pAC, IoC, Para);
        break;
    case SK_RLMT_STATS_UPDATE:    /* From PNMI. */
        SkRlmtEvtStatsUpdate(pAC, IoC, Para);
        break;
    case SK_RLMT_PREFPORT_CHANGE:    /* From PNMI. */
        SkRlmtEvtPrefportChange(pAC, IoC, Para);
        break;
    case SK_RLMT_MODE_CHANGE:    /* From PNMI. */
        SkRlmtEvtModeChange(pAC, IoC, Para);
        break;
    case SK_RLMT_SET_NETS:    /* From DRV. */
        SkRlmtEvtSetNets(pAC, IoC, Para);
        break;

    /* ----- Unknown events ----- */

    default:    /* Create error log entry. */
        SK_DBG_MSG(pAC, SK_DBGMOD_RLMT, SK_DBGCAT_CTRL,
            ("Unknown RLMT Event %d.\n", Event))
        SK_ERR_LOG(pAC, SK_ERRCL_SW, SKERR_RLMT_E003, SKERR_RLMT_E003_MSG);
        break;
    }    /* switch() */

    return (0);       
}    /* SkRlmtEvent */

#ifdef __cplusplus
}
#endif    /* __cplusplus */

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0198 ]--